mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
35 Commits
v2.267.0
...
users/tihu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8f3726265 | ||
|
|
ef72239ff8 | ||
|
|
993357df7d | ||
|
|
c62ab23bdd | ||
|
|
9d48d2be87 | ||
|
|
69aa8d8984 | ||
|
|
7da6739eae | ||
|
|
58afa42109 | ||
|
|
3dc52b28af | ||
|
|
993edc3172 | ||
|
|
6395efe7e0 | ||
|
|
e7b0844772 | ||
|
|
d5a5550649 | ||
|
|
3d0147d322 | ||
|
|
bd1f245aac | ||
|
|
005f1c15b1 | ||
|
|
da3cb5506f | ||
|
|
32d439070b | ||
|
|
ec9f8f1682 | ||
|
|
0921af735a | ||
|
|
1cc3c08cf2 | ||
|
|
f9dca15c63 | ||
|
|
0877d9a533 | ||
|
|
d5e40c6a60 | ||
|
|
391bc35bb9 | ||
|
|
e4267b8434 | ||
|
|
2709cbc0ea | ||
|
|
5e0cde8649 | ||
|
|
cb2b323781 | ||
|
|
6c3958f365 | ||
|
|
9d7bd4706b | ||
|
|
5822a38c39 | ||
|
|
d42c9da2d7 | ||
|
|
121deedeb5 | ||
|
|
a0942ed345 |
43
.github/workflows/build.yml
vendored
43
.github/workflows/build.yml
vendored
@@ -1,9 +1,10 @@
|
|||||||
name: Runner CI
|
name: Runner CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
- releases/*
|
- releases/*
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
@@ -17,28 +18,12 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
|
runtime: [ linux-x64 ]
|
||||||
include:
|
include:
|
||||||
- runtime: linux-x64
|
- runtime: linux-x64
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
devScript: ./dev.sh
|
devScript: ./dev.sh
|
||||||
|
|
||||||
- runtime: linux-arm64
|
|
||||||
os: ubuntu-latest
|
|
||||||
devScript: ./dev.sh
|
|
||||||
|
|
||||||
- runtime: linux-arm
|
|
||||||
os: ubuntu-latest
|
|
||||||
devScript: ./dev.sh
|
|
||||||
|
|
||||||
- runtime: osx-x64
|
|
||||||
os: macOS-latest
|
|
||||||
devScript: ./dev.sh
|
|
||||||
|
|
||||||
- runtime: win-x64
|
|
||||||
os: windows-latest
|
|
||||||
devScript: ./dev
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
@@ -49,13 +34,6 @@ jobs:
|
|||||||
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
|
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
|
||||||
working-directory: src
|
working-directory: src
|
||||||
|
|
||||||
# Run tests
|
|
||||||
- name: L0
|
|
||||||
run: |
|
|
||||||
${{ matrix.devScript }} test
|
|
||||||
working-directory: src
|
|
||||||
if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm'
|
|
||||||
|
|
||||||
# Create runner package tar.gz/zip
|
# Create runner package tar.gz/zip
|
||||||
- name: Package Release
|
- name: Package Release
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
@@ -70,3 +48,18 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: runner-package-${{ matrix.runtime }}
|
name: runner-package-${{ matrix.runtime }}
|
||||||
path: _package
|
path: _package
|
||||||
|
|
||||||
|
- name: Build old version
|
||||||
|
run: |
|
||||||
|
echo 2.270.0 > runnerversion
|
||||||
|
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
|
||||||
|
${{ matrix.devScript }} package Release
|
||||||
|
working-directory: src
|
||||||
|
|
||||||
|
# Upload runner package tar.gz/zip as artifact
|
||||||
|
- name: Publish Artifact old
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: runner-package-${{ matrix.runtime }}-old
|
||||||
|
path: _package
|
||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/master'
|
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim
|
||||||
|
|
||||||
|
ENV RUNNER_CONFIG_URL=""
|
||||||
|
ENV GITHUB_PAT=""
|
||||||
|
ENV RUNNER_NAME=""
|
||||||
|
ENV RUNNER_GROUP=""
|
||||||
|
ENV RUNNER_LABELS=""
|
||||||
|
# ENV GITHUB_RUNNER_SCOPE=""
|
||||||
|
# ENV GITHUB_SERVER_URL=""
|
||||||
|
# ENV GITHUB_API_URL=""
|
||||||
|
# ENV K8S_HOST_IP=""
|
||||||
|
|
||||||
|
RUN apt-get update --fix-missing \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
curl \
|
||||||
|
jq \
|
||||||
|
apt-utils \
|
||||||
|
apt-transport-https \
|
||||||
|
unzip \
|
||||||
|
net-tools\
|
||||||
|
gnupg2\
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install kubectl
|
||||||
|
RUN curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
|
||||||
|
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list && \
|
||||||
|
apt-get update && apt-get -y install --no-install-recommends kubectl
|
||||||
|
|
||||||
|
# Install docker
|
||||||
|
RUN curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
|
RUN sh get-docker.sh
|
||||||
|
|
||||||
|
|
||||||
|
# Allow runner to run as root
|
||||||
|
ENV RUNNER_ALLOW_RUNASROOT=1
|
||||||
|
|
||||||
|
# Directory for runner to operate in
|
||||||
|
RUN mkdir /actions-runner
|
||||||
|
WORKDIR /actions-runner
|
||||||
|
COPY ./src/Misc/download-runner.sh /actions-runner/download-runner.sh
|
||||||
|
COPY ./src/Misc/entrypoint.sh /actions-runner/entrypoint.sh
|
||||||
|
# COPY ./src/Misc/jobstart.sh /actions-runner/jobstart.sh
|
||||||
|
# COPY ./src/Misc/jobrunning.sh /actions-runner/jobrunning.sh
|
||||||
|
# COPY ./src/Misc/jobcomplete.sh /actions-runner/jobcomplete.sh
|
||||||
|
COPY ./src/Misc/runner_lifecycle.sh /actions-runner/runner_lifecycle.sh
|
||||||
|
|
||||||
|
RUN /actions-runner/download-runner.sh
|
||||||
|
RUN rm -f /actions-runner/download-runner.sh
|
||||||
|
|
||||||
|
# ENV _INTERNAL_JOBSTART_NOTIFICATION=/actions-runner/jobstart.sh
|
||||||
|
# ENV _INTERNAL_JOBRUNNING_NOTIFICATION=/actions-runner/jobrunning.sh
|
||||||
|
# ENV _INTERNAL_JOBCOMPLETE_NOTIFICATION=/actions-runner/jobcomplete.sh
|
||||||
|
ENV _INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION=/actions-runner/runner_lifecycle.sh
|
||||||
|
|
||||||
|
|
||||||
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
46
Dockerfile.dind
Normal file
46
Dockerfile.dind
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
FROM docker:19.03
|
||||||
|
|
||||||
|
# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies
|
||||||
|
RUN set -eux; \
|
||||||
|
apk add --no-cache \
|
||||||
|
btrfs-progs \
|
||||||
|
e2fsprogs \
|
||||||
|
e2fsprogs-extra \
|
||||||
|
iptables \
|
||||||
|
openssl \
|
||||||
|
shadow-uidmap \
|
||||||
|
xfsprogs \
|
||||||
|
xz \
|
||||||
|
# pigz: https://github.com/moby/moby/pull/35697 (faster gzip implementation)
|
||||||
|
pigz \
|
||||||
|
; \
|
||||||
|
# only install zfs if it's available for the current architecture
|
||||||
|
# https://git.alpinelinux.org/cgit/aports/tree/main/zfs/APKBUILD?h=3.6-stable#n9 ("all !armhf !ppc64le" as of 2017-11-01)
|
||||||
|
# "apk info XYZ" exits with a zero exit code but no output when the package exists but not for this arch
|
||||||
|
if zfs="$(apk info --no-cache --quiet zfs)" && [ -n "$zfs" ]; then \
|
||||||
|
apk add --no-cache zfs; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO aufs-tools
|
||||||
|
|
||||||
|
# set up subuid/subgid so that "--userns-remap=default" works out-of-the-box
|
||||||
|
RUN set -x \
|
||||||
|
&& addgroup -S dockremap \
|
||||||
|
&& adduser -S -G dockremap dockremap \
|
||||||
|
&& echo 'dockremap:165536:65536' >> /etc/subuid \
|
||||||
|
&& echo 'dockremap:165536:65536' >> /etc/subgid
|
||||||
|
|
||||||
|
# https://github.com/docker/docker/tree/master/hack/dind
|
||||||
|
ENV DIND_COMMIT ed89041433a031cafc0a0f19cfe573c31688d377
|
||||||
|
|
||||||
|
RUN set -eux; \
|
||||||
|
wget -O /usr/local/bin/dind "https://raw.githubusercontent.com/docker/docker/${DIND_COMMIT}/hack/dind"; \
|
||||||
|
chmod +x /usr/local/bin/dind
|
||||||
|
|
||||||
|
COPY dockerd-entrypoint.sh /usr/local/bin/
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
EXPOSE 6788 6789
|
||||||
|
|
||||||
|
ENTRYPOINT ["dockerd-entrypoint.sh"]
|
||||||
|
CMD []
|
||||||
14
autoscalev0.yaml
Normal file
14
autoscalev0.yaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: auto-scale-runners
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
maxRunnerLimit: 5
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
configURL: https://github.com/bbq-beets/ting-test
|
||||||
|
githubTokenSecretKeyRef:
|
||||||
|
name: githubtoken
|
||||||
|
key: GITHUB_PAT
|
||||||
|
|
||||||
63
deployment.yaml
Normal file
63
deployment.yaml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: runner-pod
|
||||||
|
labels:
|
||||||
|
name: runner-pod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: runner-pod
|
||||||
|
image: huangtingluo/autoscale-runner:v0.0
|
||||||
|
imagePullPolicy: Always
|
||||||
|
env:
|
||||||
|
- name: GITHUB_PAT
|
||||||
|
value: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
- name: GITHUB_RUNNER_SCOPE
|
||||||
|
value: monalisa/main123
|
||||||
|
- name: K8S_HOST_IP
|
||||||
|
value: "192.168.120.1"
|
||||||
|
|
||||||
|
# apiVersion: apps/v1
|
||||||
|
# kind: Deployment
|
||||||
|
# metadata:
|
||||||
|
# name: runner-deployment
|
||||||
|
# spec:
|
||||||
|
# replicas: 1
|
||||||
|
# selector:
|
||||||
|
# matchLabels:
|
||||||
|
# app: runners
|
||||||
|
# template:
|
||||||
|
# metadata:
|
||||||
|
# labels:
|
||||||
|
# app: runners
|
||||||
|
# spec:
|
||||||
|
# # hostNetwork: true
|
||||||
|
# # volumes:
|
||||||
|
# # - name: docker-storage
|
||||||
|
# # emptyDir: {}
|
||||||
|
# # containers:
|
||||||
|
# # - name: docker-host
|
||||||
|
# # image: docker:18.05-dind
|
||||||
|
# # imagePullPolicy: Always
|
||||||
|
# # securityContext:
|
||||||
|
# # privileged: true
|
||||||
|
# # volumeMounts:
|
||||||
|
# # - name: docker-storage
|
||||||
|
# # mountPath: /var/lib/docker
|
||||||
|
|
||||||
|
# # hostNetwork: true
|
||||||
|
# containers:
|
||||||
|
# - name: runner
|
||||||
|
# image: huangtingluo/autoscale-runner:v0.0
|
||||||
|
# imagePullPolicy: Always
|
||||||
|
# env:
|
||||||
|
# - name: GITHUB_PAT
|
||||||
|
# value: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
# - name: GITHUB_RUNNER_SCOPE
|
||||||
|
# value: monalisa/main123
|
||||||
|
# - name: K8S_HOST_IP
|
||||||
|
# value: "192.168.120.1"
|
||||||
|
# resources:
|
||||||
|
# limits:
|
||||||
|
# memory: "128Mi"
|
||||||
|
# cpu: "500m"
|
||||||
186
dockerd-entrypoint.sh
Executable file
186
dockerd-entrypoint.sh
Executable file
@@ -0,0 +1,186 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
_tls_ensure_private() {
|
||||||
|
local f="$1"; shift
|
||||||
|
[ -s "$f" ] || openssl genrsa -out "$f" 4096
|
||||||
|
}
|
||||||
|
_tls_san() {
|
||||||
|
{
|
||||||
|
ip -oneline address | awk '{ gsub(/\/.+$/, "", $4); print "IP:" $4 }'
|
||||||
|
{
|
||||||
|
cat /etc/hostname
|
||||||
|
echo 'docker'
|
||||||
|
echo 'localhost'
|
||||||
|
hostname -f
|
||||||
|
hostname -s
|
||||||
|
} | sed 's/^/DNS:/'
|
||||||
|
[ -z "${DOCKER_TLS_SAN:-}" ] || echo "$DOCKER_TLS_SAN"
|
||||||
|
} | sort -u | xargs printf '%s,' | sed "s/,\$//"
|
||||||
|
}
|
||||||
|
_tls_generate_certs() {
|
||||||
|
local dir="$1"; shift
|
||||||
|
|
||||||
|
# if ca/key.pem || !ca/cert.pem, generate CA public if necessary
|
||||||
|
# if ca/key.pem, generate server public
|
||||||
|
# if ca/key.pem, generate client public
|
||||||
|
# (regenerating public certs every startup to account for SAN/IP changes and/or expiration)
|
||||||
|
|
||||||
|
# https://github.com/FiloSottile/mkcert/issues/174
|
||||||
|
local certValidDays='825'
|
||||||
|
|
||||||
|
if [ -s "$dir/ca/key.pem" ] || [ ! -s "$dir/ca/cert.pem" ]; then
|
||||||
|
# if we either have a CA private key or do *not* have a CA public key, then we should create/manage the CA
|
||||||
|
mkdir -p "$dir/ca"
|
||||||
|
_tls_ensure_private "$dir/ca/key.pem"
|
||||||
|
openssl req -new -key "$dir/ca/key.pem" \
|
||||||
|
-out "$dir/ca/cert.pem" \
|
||||||
|
-subj '/CN=docker:dind CA' -x509 -days "$certValidDays"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s "$dir/ca/key.pem" ]; then
|
||||||
|
# if we have a CA private key, we should create/manage a server key
|
||||||
|
mkdir -p "$dir/server"
|
||||||
|
_tls_ensure_private "$dir/server/key.pem"
|
||||||
|
openssl req -new -key "$dir/server/key.pem" \
|
||||||
|
-out "$dir/server/csr.pem" \
|
||||||
|
-subj '/CN=docker:dind server'
|
||||||
|
cat > "$dir/server/openssl.cnf" <<-EOF
|
||||||
|
[ x509_exts ]
|
||||||
|
subjectAltName = $(_tls_san)
|
||||||
|
EOF
|
||||||
|
openssl x509 -req \
|
||||||
|
-in "$dir/server/csr.pem" \
|
||||||
|
-CA "$dir/ca/cert.pem" \
|
||||||
|
-CAkey "$dir/ca/key.pem" \
|
||||||
|
-CAcreateserial \
|
||||||
|
-out "$dir/server/cert.pem" \
|
||||||
|
-days "$certValidDays" \
|
||||||
|
-extfile "$dir/server/openssl.cnf" \
|
||||||
|
-extensions x509_exts
|
||||||
|
cp "$dir/ca/cert.pem" "$dir/server/ca.pem"
|
||||||
|
openssl verify -CAfile "$dir/server/ca.pem" "$dir/server/cert.pem"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s "$dir/ca/key.pem" ]; then
|
||||||
|
# if we have a CA private key, we should create/manage a client key
|
||||||
|
mkdir -p "$dir/client"
|
||||||
|
_tls_ensure_private "$dir/client/key.pem"
|
||||||
|
chmod 0644 "$dir/client/key.pem" # openssl defaults to 0600 for the private key, but this one needs to be shared with arbitrary client contexts
|
||||||
|
openssl req -new \
|
||||||
|
-key "$dir/client/key.pem" \
|
||||||
|
-out "$dir/client/csr.pem" \
|
||||||
|
-subj '/CN=docker:dind client'
|
||||||
|
cat > "$dir/client/openssl.cnf" <<-'EOF'
|
||||||
|
[ x509_exts ]
|
||||||
|
extendedKeyUsage = clientAuth
|
||||||
|
EOF
|
||||||
|
openssl x509 -req \
|
||||||
|
-in "$dir/client/csr.pem" \
|
||||||
|
-CA "$dir/ca/cert.pem" \
|
||||||
|
-CAkey "$dir/ca/key.pem" \
|
||||||
|
-CAcreateserial \
|
||||||
|
-out "$dir/client/cert.pem" \
|
||||||
|
-days "$certValidDays" \
|
||||||
|
-extfile "$dir/client/openssl.cnf" \
|
||||||
|
-extensions x509_exts
|
||||||
|
cp "$dir/ca/cert.pem" "$dir/client/ca.pem"
|
||||||
|
openssl verify -CAfile "$dir/client/ca.pem" "$dir/client/cert.pem"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# no arguments passed
|
||||||
|
# or first arg is `-f` or `--some-option`
|
||||||
|
if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then
|
||||||
|
# set "dockerSocket" to the default "--host" *unix socket* value (for both standard or rootless)
|
||||||
|
uid="$(id -u)"
|
||||||
|
if [ "$uid" = '0' ]; then
|
||||||
|
dockerSocket='unix:///var/run/docker.sock'
|
||||||
|
else
|
||||||
|
# if we're not root, we must be trying to run rootless
|
||||||
|
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
|
||||||
|
dockerSocket="unix://$XDG_RUNTIME_DIR/docker.sock"
|
||||||
|
fi
|
||||||
|
case "${DOCKER_HOST:-}" in
|
||||||
|
unix://*)
|
||||||
|
dockerSocket="$DOCKER_HOST"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# add our default arguments
|
||||||
|
if [ -n "${DOCKER_TLS_CERTDIR:-}" ] \
|
||||||
|
&& _tls_generate_certs "$DOCKER_TLS_CERTDIR" \
|
||||||
|
&& [ -s "$DOCKER_TLS_CERTDIR/server/ca.pem" ] \
|
||||||
|
&& [ -s "$DOCKER_TLS_CERTDIR/server/cert.pem" ] \
|
||||||
|
&& [ -s "$DOCKER_TLS_CERTDIR/server/key.pem" ] \
|
||||||
|
; then
|
||||||
|
# generate certs and use TLS if requested/possible (default in 19.03+)
|
||||||
|
set -- dockerd \
|
||||||
|
--host="$dockerSocket" \
|
||||||
|
--host=tcp://0.0.0.0:6789 \
|
||||||
|
--tlsverify \
|
||||||
|
--tlscacert "$DOCKER_TLS_CERTDIR/server/ca.pem" \
|
||||||
|
--tlscert "$DOCKER_TLS_CERTDIR/server/cert.pem" \
|
||||||
|
--tlskey "$DOCKER_TLS_CERTDIR/server/key.pem" \
|
||||||
|
"$@"
|
||||||
|
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:6789:6789/tcp"
|
||||||
|
else
|
||||||
|
# TLS disabled (-e DOCKER_TLS_CERTDIR='') or missing certs
|
||||||
|
set -- dockerd \
|
||||||
|
--host="$dockerSocket" \
|
||||||
|
--host=tcp://0.0.0.0:6788 \
|
||||||
|
"$@"
|
||||||
|
DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS="${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} -p 0.0.0.0:6788:6788/tcp"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = 'dockerd' ]; then
|
||||||
|
# explicitly remove Docker's default PID file to ensure that it can start properly if it was stopped uncleanly (and thus didn't clean up the PID file)
|
||||||
|
find /run /var/run -iname 'docker*.pid' -delete || :
|
||||||
|
|
||||||
|
uid="$(id -u)"
|
||||||
|
if [ "$uid" != '0' ]; then
|
||||||
|
# if we're not root, we must be trying to run rootless
|
||||||
|
if ! command -v rootlesskit > /dev/null; then
|
||||||
|
echo >&2 "error: attempting to run rootless dockerd but missing 'rootlesskit' (perhaps the 'docker:dind-rootless' image variant is intended?)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
user="$(id -un 2>/dev/null || :)"
|
||||||
|
if ! grep -qE "^($uid${user:+|$user}):" /etc/subuid || ! grep -qE "^($uid${user:+|$user}):" /etc/subgid; then
|
||||||
|
echo >&2 "error: attempting to run rootless dockerd but missing necessary entries in /etc/subuid and/or /etc/subgid for $uid"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
: "${XDG_RUNTIME_DIR:=/run/user/$uid}"
|
||||||
|
export XDG_RUNTIME_DIR
|
||||||
|
if ! mkdir -p "$XDG_RUNTIME_DIR" || [ ! -w "$XDG_RUNTIME_DIR" ] || ! mkdir -p "$HOME/.local/share/docker" || [ ! -w "$HOME/.local/share/docker" ]; then
|
||||||
|
echo >&2 "error: attempting to run rootless dockerd but need writable HOME ($HOME) and XDG_RUNTIME_DIR ($XDG_RUNTIME_DIR) for user $uid"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -f /proc/sys/kernel/unprivileged_userns_clone ] && unprivClone="$(cat /proc/sys/kernel/unprivileged_userns_clone)" && [ "$unprivClone" != '1' ]; then
|
||||||
|
echo >&2 "error: attempting to run rootless dockerd but need 'kernel.unprivileged_userns_clone' (/proc/sys/kernel/unprivileged_userns_clone) set to 1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -f /proc/sys/user/max_user_namespaces ] && maxUserns="$(cat /proc/sys/user/max_user_namespaces)" && [ "$maxUserns" = '0' ]; then
|
||||||
|
echo >&2 "error: attempting to run rootless dockerd but need 'user.max_user_namespaces' (/proc/sys/user/max_user_namespaces) set to a sufficiently large value"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# TODO overlay support detection?
|
||||||
|
exec rootlesskit \
|
||||||
|
--net="${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:-vpnkit}" \
|
||||||
|
--mtu="${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:-1500}" \
|
||||||
|
--disable-host-loopback \
|
||||||
|
--port-driver=builtin \
|
||||||
|
--copy-up=/etc \
|
||||||
|
--copy-up=/run \
|
||||||
|
${DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS:-} \
|
||||||
|
"$@"
|
||||||
|
elif [ -x '/usr/local/bin/dind' ]; then
|
||||||
|
# if we have the (mostly defunct now) Docker-in-Docker wrapper script, use it
|
||||||
|
set -- '/usr/local/bin/dind' "$@"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# if it isn't `dockerd` we're trying to run, pass it through `docker-entrypoint.sh` so it gets `DOCKER_HOST` set appropriately too
|
||||||
|
set -- docker-entrypoint.sh "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
||||||
293
docs/adrs/0549-composite-run-steps.md
Normal file
293
docs/adrs/0549-composite-run-steps.md
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
# ADR 0549: Composite Run Steps
|
||||||
|
|
||||||
|
**Date**: 2020-06-17
|
||||||
|
|
||||||
|
**Status**: Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Customers want to be able to compose actions from actions (ex: https://github.com/actions/runner/issues/438)
|
||||||
|
|
||||||
|
An important step towards meeting this goal is to build in functionality for actions where users can simply execute any number of steps.
|
||||||
|
|
||||||
|
### Guiding Principles
|
||||||
|
|
||||||
|
We don't want the workflow author to need to know how the internal workings of the action work. Users shouldn't know the internal workings of the composite action (for example, `default.shell` and `default.workingDir` should not be inherited from the workflow file to the action file). When deciding how to design certain parts of composite run steps, we want to think one logical step from the consumer.
|
||||||
|
|
||||||
|
A composite action is treated as **one** individual job step (this is known as encapsulation).
|
||||||
|
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**In this ADR, we only support running multiple run steps in an Action.** In doing so, we build in support for mapping and flowing the inputs, outputs, and env variables (ex: All nested steps should have access to its parents' input variables and nested steps can overwrite the input variables).
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
Example `workflow.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- id: step1
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
- id: step2
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: user/composite@v1
|
||||||
|
- name: workflow step 1
|
||||||
|
run: echo hello world 3
|
||||||
|
- name: workflow step 2
|
||||||
|
run: echo hello world 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- run: pip install -r requirements.txt
|
||||||
|
- run: npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Example Output
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
[npm installation output]
|
||||||
|
[pip requirements output]
|
||||||
|
echo hello world 3
|
||||||
|
echo hello world 4
|
||||||
|
```
|
||||||
|
|
||||||
|
We add a token called "composite" which allows our Runner code to process composite actions. By invoking "using: composite", our Runner code then processes the "steps" attribute, converts this template code to a list of steps, and finally runs each run step sequentially. If any step fails and there are no `if` conditions defined, the whole composite action job fails.
|
||||||
|
|
||||||
|
### Inputs
|
||||||
|
|
||||||
|
Example `workflow.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- id: foo
|
||||||
|
uses: user/composite@v1
|
||||||
|
with:
|
||||||
|
your_name: "Octocat"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
inputs:
|
||||||
|
your_name:
|
||||||
|
description: 'Your name'
|
||||||
|
default: 'Ethan'
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- run: echo hello ${{ inputs.your_name }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
hello Octocat
|
||||||
|
```
|
||||||
|
|
||||||
|
Each input variable in the composite action is only viewable in its own scope.
|
||||||
|
|
||||||
|
### Outputs
|
||||||
|
|
||||||
|
Example `workflow.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
...
|
||||||
|
steps:
|
||||||
|
- id: foo
|
||||||
|
uses: user/composite@v1
|
||||||
|
- run: echo random-number ${{ steps.foo.outputs.random-number }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
outputs:
|
||||||
|
random-number:
|
||||||
|
description: "Random number"
|
||||||
|
value: ${{ steps.random-number-generator.outputs.random-id }}
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: random-number-generator
|
||||||
|
run: echo "::set-output name=random-number::$(echo $RANDOM)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
::set-output name=my-output::43243
|
||||||
|
random-number 43243
|
||||||
|
```
|
||||||
|
|
||||||
|
Each of the output variables from the composite action is viewable from the workflow file that uses the composite action. In other words, every child action output(s) is viewable only by its parent using dot notation (ex `steps.foo.outputs.random-number`).
|
||||||
|
|
||||||
|
Moreover, the output ids are only accessible within the scope where it was defined. Note that in the example above, in our `workflow.yml` file, it should not have access to output id (i.e. `random-id`). The reason why we are doing this is because we don't want to require the workflow author to know the internal workings of the composite action.
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
Similar to the workflow file, the composite action has access to the [same context objects](https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts) (ex: `github`, `env`, `strategy`).
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
|
||||||
|
In the Composite Action, you'll only be able to use `::set-env::` to set environment variables just like you could with other actions.
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
**Note** : This feature will be focused on in a future ADR.
|
||||||
|
|
||||||
|
We'll pass the secrets from the composite action's parents (ex: the workflow file) to the composite action. Secrets can be created in the composite action with the secrets context. In the actions yaml, we'll automatically mask the secret.
|
||||||
|
|
||||||
|
|
||||||
|
### If Condition
|
||||||
|
|
||||||
|
Example `workflow.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- run: exit 1
|
||||||
|
- uses: user/composite@v1 # <--- this will run, as it's marked as always runing
|
||||||
|
if: always()
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- run: echo "just succeeding"
|
||||||
|
- run: echo "I will run, as my current scope is succeeding"
|
||||||
|
if: success()
|
||||||
|
- run: exit 1
|
||||||
|
- run: echo "I will not run, as my current scope is now failing"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the paragraph below for a rudimentary approach (thank you to @cybojenix for the idea, example, and explanation for this approach):
|
||||||
|
|
||||||
|
The `if` statement in the parent (in the example above, this is the `workflow.yml`) shows whether or not we should run the composite action. So, our composite action will run since the `if` condition for running the composite action is `always()`.
|
||||||
|
|
||||||
|
**Note that the if condition on the parent does not propagate to the rest of its children though.**
|
||||||
|
|
||||||
|
In the child action (in this example, this is the `action.yml`), it starts with a clean slate (in other words, no imposing if conditions). Similar to the logic in the paragraph above, `echo "I will run, as my current scope is succeeding"` will run since the `if` condition checks if the previous steps **within this composite action** has not failed. `run: echo "I will not run, as my current scope is now failing"` will not run since the previous step resulted in an error and by default, the if expression is set to `success()` if the if condition is not set for a step.
|
||||||
|
|
||||||
|
|
||||||
|
What if a step has `cancelled()`? We do the opposite of our approach above if `cancelled()` is used for any of our composite run steps. We will cancel any step that has this condition if the workflow is cancelled at all.
|
||||||
|
|
||||||
|
### Timeout-minutes
|
||||||
|
|
||||||
|
Example `workflow.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- id: bar
|
||||||
|
uses: user/test@v1
|
||||||
|
timeout-minutes: 50
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: foo1
|
||||||
|
run: echo test 1
|
||||||
|
timeout-minutes: 10
|
||||||
|
- id: foo2
|
||||||
|
run: echo test 2
|
||||||
|
- id: foo3
|
||||||
|
run: echo test 3
|
||||||
|
timeout-minutes: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
A composite action in its entirety is a job. You can set both timeout-minutes for the whole composite action or its steps as long as the the sum of the `timeout-minutes` for each composite action step that has the attribute `timeout-minutes` is less than or equals to `timeout-minutes` for the composite action. There is no default timeout-minutes for each composite action step.
|
||||||
|
|
||||||
|
If the time taken for any of the steps in combination or individually exceed the whole composite action `timeout-minutes` attribute, the whole job will fail (1). If an individual step exceeds its own `timeout-minutes` attribute but the total time that has been used including this step is below the overall composite action `timeout-minutes`, the individual step will fail but the rest of the steps will run based on their own `timeout-minutes` attribute (they will still abide by condition (1) though).
|
||||||
|
|
||||||
|
For reference, in the example above, if the composite step `foo1` takes 11 minutes to run, that step will fail but the rest of the steps, `foo1` and `foo2`, will proceed as long as their total runtime with the previous failed `foo1` action is less than the composite action's `timeout-minutes` (50 minutes). If the composite step `foo2` takes 51 minutes to run, it will cause the whole composite action job to fail. I
|
||||||
|
|
||||||
|
The rationale behind this is that users can configure their steps with the `if` condition to conditionally set how steps rely on each other. Due to the additional capabilities that are offered with combining `timeout-minutes` and/or `if`, we wanted the `timeout-minutes` condition to be as dumb as possible and not effect other steps.
|
||||||
|
|
||||||
|
[Usage limits still apply](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions?query=if%28%29#usage-limits)
|
||||||
|
|
||||||
|
|
||||||
|
### Continue-on-error
|
||||||
|
|
||||||
|
Example `workflow.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- run: exit 1
|
||||||
|
- id: bar
|
||||||
|
uses: user/test@v1
|
||||||
|
continue-on-error: false
|
||||||
|
- id: foo
|
||||||
|
run: echo "Hello World" <------- This step will not run
|
||||||
|
```
|
||||||
|
|
||||||
|
Example `user/composite/action.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- run: exit 1
|
||||||
|
continue-on-error: true
|
||||||
|
- run: echo "Hello World 2" <----- This step will run
|
||||||
|
```
|
||||||
|
|
||||||
|
If any of the steps fail in the composite action and the `continue-on-error` is set to `false` for the whole composite action step in the workflow file, then the steps below it will run. On the flip side, if `continue-on-error` is set to `true` for the whole composite action step in the workflow file, the next job step will run.
|
||||||
|
|
||||||
|
For the composite action steps, it follows the same logic as above. In this example, `"Hello World 2"` will be outputted because the previous step has `continue-on-error` set to `true` although that previous step errored.
|
||||||
|
|
||||||
|
### Defaults
|
||||||
|
We will not support "defaults" in a composite action.
|
||||||
|
|
||||||
|
### Shell and Working-directory
|
||||||
|
For each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. These attributes are optional for each run step - by default, the `shell` is set to whatever default value is associated with the runner os (ex: bash =\> Mac). Moreover, the composite action author can map in values from the `inputs` for it's `shell` and `working-directory` attributes at the step level for an action.
|
||||||
|
|
||||||
|
For example,
|
||||||
|
|
||||||
|
`action.yml`
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
inputs:
|
||||||
|
shell_1:
|
||||||
|
description: 'Your name'
|
||||||
|
default: 'pwsh'
|
||||||
|
steps:
|
||||||
|
- run: echo 1
|
||||||
|
shell: ${{ inputs.shell_1 }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note, the workflow file and action file are treated as separate entities. **So, the workflow `defaults` will never change the `shell` and `working-directory` value in the run steps in a composite action.** Note, `defaults` in a workflow only apply to run steps not "uses" steps (steps that use an action).
|
||||||
|
|
||||||
|
### Visualizing Composite Action in the GitHub Actions UI
|
||||||
|
We want all the composite action's steps to be condensed into the original composite action node.
|
||||||
|
|
||||||
|
Here is a visual represenation of the [first example](#Steps)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
| composite_action_node |
|
||||||
|
| echo hello world 1 |
|
||||||
|
| echo hello world 2 |
|
||||||
|
| echo hello world 3 |
|
||||||
|
| echo hello world 4 |
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
This ADR lays the framework for eventually supporting nested Composite Actions within Composite Actions. This ADR allows for users to run multiple run steps within a GitHub Composite Action with the support of inputs, outputs, environment, and context for use in any steps as well as the if, timeout-minutes, and the continue-on-error attributes for each Composite Action step.
|
||||||
@@ -51,6 +51,19 @@ cd ./src
|
|||||||
./dev.(sh/cmd) test # run all unit tests before git commit/push
|
./dev.(sh/cmd) test # run all unit tests before git commit/push
|
||||||
```
|
```
|
||||||
|
|
||||||
|
View logs:
|
||||||
|
```bash
|
||||||
|
cd runner/_layout/_diag
|
||||||
|
ls
|
||||||
|
cat (Runner/Worker)_TIMESTAMP.log # view your log file
|
||||||
|
```
|
||||||
|
|
||||||
|
Run Runner:
|
||||||
|
```bash
|
||||||
|
cd runner/_layout
|
||||||
|
./run.sh # run your custom runner
|
||||||
|
```
|
||||||
|
|
||||||
### Editors
|
### Editors
|
||||||
|
|
||||||
[Using Visual Studio Code](https://code.visualstudio.com/)
|
[Using Visual Studio Code](https://code.visualstudio.com/)
|
||||||
|
|||||||
48
ephemeralJob.yaml
Normal file
48
ephemeralJob.yaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
apiVersion: batch.github.actions/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: cronjob-sample
|
||||||
|
spec:
|
||||||
|
schedule: "*/1 * * * *"
|
||||||
|
jobTemplate:
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
hostNetwork: true
|
||||||
|
containers:
|
||||||
|
- name: k8srunner
|
||||||
|
image: huangtingluo/autoscale-runner:v0.0
|
||||||
|
imagePullPolicy: Always
|
||||||
|
env:
|
||||||
|
- name: GITHUB_PAT
|
||||||
|
value: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
- name: GITHUB_RUNNER_SCOPE
|
||||||
|
value: monalisa/main123
|
||||||
|
restartPolicy: Never
|
||||||
|
# spec:
|
||||||
|
# containers:
|
||||||
|
# - name: hello
|
||||||
|
# image: busybox
|
||||||
|
# args:
|
||||||
|
# - /bin/sh
|
||||||
|
# - -c
|
||||||
|
# - date; echo Hello from the Kubernetes cluster
|
||||||
|
# restartPolicy: Never
|
||||||
|
# jobTemplate:
|
||||||
|
# spec:
|
||||||
|
# template:
|
||||||
|
# spec:
|
||||||
|
# hostNetwork: true
|
||||||
|
# containers:
|
||||||
|
# - name: k8srunner
|
||||||
|
# image: huangtingluo/autoscale-runner:v0.0
|
||||||
|
# imagePullPolicy: Always
|
||||||
|
# env:
|
||||||
|
# - name: GITHUB_PAT
|
||||||
|
# value: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
# - name: GITHUB_RUNNER_SCOPE
|
||||||
|
# value: monalisa/main123
|
||||||
|
# restartPolicy: Never
|
||||||
|
# backoffLimit: 1
|
||||||
|
# completions: 0
|
||||||
|
# parallelism: 3
|
||||||
56
hpa-v2.yaml
Normal file
56
hpa-v2.yaml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
items:
|
||||||
|
- apiVersion: autoscaling/v2beta2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: "2020-08-05T19:14:04Z"
|
||||||
|
name: runner-deployment
|
||||||
|
namespace: default
|
||||||
|
resourceVersion: "167447"
|
||||||
|
selfLink: /apis/autoscaling/v2beta2/namespaces/default/horizontalpodautoscalers/runner-deployment
|
||||||
|
uid: 54d86943-eca9-468c-9698-c843f6b6183a
|
||||||
|
spec:
|
||||||
|
maxReplicas: 10
|
||||||
|
metrics:
|
||||||
|
- type: Object
|
||||||
|
object:
|
||||||
|
metric:
|
||||||
|
name: test-metric
|
||||||
|
describedObject:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
name: kubernetes
|
||||||
|
target:
|
||||||
|
type: Value
|
||||||
|
value: 300m
|
||||||
|
- resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
averageUtilization: 50
|
||||||
|
type: Utilization
|
||||||
|
type: Resource
|
||||||
|
minReplicas: 1
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: runner-deployment
|
||||||
|
status:
|
||||||
|
conditions:
|
||||||
|
- lastTransitionTime: "2020-08-05T19:14:19Z"
|
||||||
|
message: the HPA controller was able to get the target's current scale
|
||||||
|
reason: SucceededGetScale
|
||||||
|
status: "True"
|
||||||
|
type: AbleToScale
|
||||||
|
- lastTransitionTime: "2020-08-05T19:14:19Z"
|
||||||
|
message: 'the HPA was unable to compute the replica count: unable to get metrics
|
||||||
|
for resource cpu: no metrics returned from resource metrics API'
|
||||||
|
reason: FailedGetResourceMetric
|
||||||
|
status: "False"
|
||||||
|
type: ScalingActive
|
||||||
|
currentMetrics: null
|
||||||
|
currentReplicas: 1
|
||||||
|
desiredReplicas: 0
|
||||||
|
kind: List
|
||||||
|
metadata:
|
||||||
|
resourceVersion: ""
|
||||||
|
selfLink: ""
|
||||||
92
job.yaml
Normal file
92
job.yaml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: pod-admin
|
||||||
|
namespace: default
|
||||||
|
rules:
|
||||||
|
- apiGroups: [ "" ]
|
||||||
|
resources: [ "pods", "pods/ephemeralcontainers", "pods/log", "pods/attach", "pods/exec"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: default-pod-admin
|
||||||
|
namespace: default
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: pod-admin
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: Job
|
||||||
|
metadata:
|
||||||
|
namespace: default
|
||||||
|
name: actions-runners
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
# hostNetwork: true
|
||||||
|
volumes:
|
||||||
|
- name: docker-storage
|
||||||
|
emptyDir: {}
|
||||||
|
- name: runner-working
|
||||||
|
emptyDir: {}
|
||||||
|
containers:
|
||||||
|
- name: docker-host
|
||||||
|
image: docker:18.05-dind
|
||||||
|
imagePullPolicy: Always
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: docker-storage
|
||||||
|
mountPath: /var/lib/docker
|
||||||
|
- mountPath: /actions-runner/_work
|
||||||
|
name: runner-working
|
||||||
|
- name: k8srunner
|
||||||
|
image: huangtingluo/autoscale-runner:v0.0
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /actions-runner/_work
|
||||||
|
name: runner-working
|
||||||
|
imagePullPolicy: Always
|
||||||
|
env:
|
||||||
|
- name: GITHUB_PAT
|
||||||
|
value: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
- name: GITHUB_RUNNER_SCOPE
|
||||||
|
value: monalisa/main123
|
||||||
|
- name: K8S_HOST_IP
|
||||||
|
value: "192.168.120.1"
|
||||||
|
- name: DOCKER_HOST
|
||||||
|
value: tcp://localhost:2375
|
||||||
|
- name: K8S_NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
- name: K8S_POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: K8S_POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: K8S_POD_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: status.podIP
|
||||||
|
- name: K8S_POD_SERVICE_ACCOUNT
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.serviceAccountName
|
||||||
|
restartPolicy: Never
|
||||||
|
backoffLimit: 1
|
||||||
|
completions: 20
|
||||||
|
parallelism: 3
|
||||||
34
prereq.yaml
Normal file
34
prereq.yaml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
namespace: default
|
||||||
|
name: pod-patcher
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""] # "" indicates the core API group
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "watch", "list", "patch"]
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: default-pod-patcher
|
||||||
|
namespace: default
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: pod-patcher
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: githubtoken
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
GITHUB_PAT: "xxx"
|
||||||
12
runner.yaml
Normal file
12
runner.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: actions.github.com/v1alpha1
|
||||||
|
kind: Runner
|
||||||
|
metadata:
|
||||||
|
name: auto-scale-runners
|
||||||
|
spec:
|
||||||
|
organization: monalisa
|
||||||
|
group: default
|
||||||
|
repository: main123
|
||||||
|
githubAdminToken: 62c13e14e947958516c103a9584f66227697c447
|
||||||
|
env:
|
||||||
|
- name: K8S_HOST_IP
|
||||||
|
value: "192.168.120.1"
|
||||||
46
runners.yaml
Normal file
46
runners.yaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
apiVersion: actions.github.com/v1alpha1
|
||||||
|
kind: AutoScaleRunner
|
||||||
|
metadata:
|
||||||
|
name: auto-scale-runners
|
||||||
|
spec:
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 5
|
||||||
|
configURL: https://github.com/TingluoHuang/example-services
|
||||||
|
githubTokenSecretKeyRef:
|
||||||
|
name: githubtoken
|
||||||
|
key: GITHUB_PAT
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
setupDockerInDocker: true
|
||||||
|
imagePullPolicy: Always
|
||||||
|
runnerUpdateHandler:
|
||||||
|
containers:
|
||||||
|
- name: update-image
|
||||||
|
image: huangtingluo/workflow_dispatch:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
env:
|
||||||
|
- name: GITHUB_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: githubtoken
|
||||||
|
key: GITHUB_PAT
|
||||||
|
- name: GITHUB_OWNER
|
||||||
|
value: tingluohuang
|
||||||
|
- name: GITHUB_REPO
|
||||||
|
value: "workflow_dispatch"
|
||||||
|
- name: GITHUB_EXTRA_CURL_ARG
|
||||||
|
value: "-v"
|
||||||
|
- name: GITHUB_WORKFLOW
|
||||||
|
value: "2539181"
|
||||||
|
- name: GITHUB_WORKFLOW_INPUTS
|
||||||
|
value: "{\"test_input\":\"test\"}"
|
||||||
|
|
||||||
|
# - name: GITHUB_REPO
|
||||||
|
# value: "k8s-runner-image"
|
||||||
|
# - name: GITHUB_EXTRA_CURL_ARG
|
||||||
|
# value: "-v"
|
||||||
|
# - name: GITHUB_WORKFLOW
|
||||||
|
# value: "docker-publish.yml"
|
||||||
|
# - name: GITHUB_WORKFLOW_INPUTS
|
||||||
|
# value: "{\"runnerDownloadUrl\":\"https://github.com/TingluoHuang/runner/releases/download/test/actions-runner-linux-x64-2.299.0.tar.gz\"}"
|
||||||
|
|
||||||
379
src/Misc/dotnet-install.ps1
vendored
379
src/Misc/dotnet-install.ps1
vendored
@@ -154,7 +154,16 @@ function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [in
|
|||||||
function Get-Machine-Architecture() {
|
function Get-Machine-Architecture() {
|
||||||
Say-Invocation $MyInvocation
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
# possible values: amd64, x64, x86, arm64, arm
|
# On PS x86, PROCESSOR_ARCHITECTURE reports x86 even on x64 systems.
|
||||||
|
# To get the correct architecture, we need to use PROCESSOR_ARCHITEW6432.
|
||||||
|
# PS x64 doesn't define this, so we fall back to PROCESSOR_ARCHITECTURE.
|
||||||
|
# Possible values: amd64, x64, x86, arm64, arm
|
||||||
|
|
||||||
|
if( $ENV:PROCESSOR_ARCHITEW6432 -ne $null )
|
||||||
|
{
|
||||||
|
return $ENV:PROCESSOR_ARCHITEW6432
|
||||||
|
}
|
||||||
|
|
||||||
return $ENV:PROCESSOR_ARCHITECTURE
|
return $ENV:PROCESSOR_ARCHITECTURE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,194 +695,194 @@ Say "Installation finished"
|
|||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
# SIG # Begin signature block
|
# SIG # Begin signature block
|
||||||
# MIIjkQYJKoZIhvcNAQcCoIIjgjCCI34CAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
# MIIjhwYJKoZIhvcNAQcCoIIjeDCCI3QCAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
||||||
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
||||||
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAwp4UsNdAkvwY3
|
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAiKYSY4KtkeThH
|
||||||
# VhbuN9D6NGOz+qNqW2+62YubWa4qJaCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
|
# d5M1aXqv1K0/pff07QwfUbYZ/qX5LqCCDYUwggYDMIID66ADAgECAhMzAAABiK9S
|
||||||
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
|
# 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
|
||||||
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
|
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
|
||||||
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
|
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
|
||||||
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
|
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw
|
||||||
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
|
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
|
||||||
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
|
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
|
||||||
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||||
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
|
# AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0
|
||||||
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
|
# t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs
|
||||||
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
|
# 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd
|
||||||
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
|
# vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv
|
||||||
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
|
# V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W
|
||||||
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
|
# MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
|
||||||
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
|
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w
|
||||||
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
|
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
|
||||||
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
|
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW
|
||||||
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
|
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
|
||||||
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
|
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
|
||||||
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
|
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
|
||||||
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
|
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
|
||||||
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
|
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
|
||||||
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
|
# ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q
|
||||||
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
|
# qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X
|
||||||
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
|
# 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P
|
||||||
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
|
# mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM
|
||||||
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
|
# i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT
|
||||||
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
|
# GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz
|
||||||
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
|
# LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM
|
||||||
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
|
# SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa
|
||||||
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
|
# 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV
|
||||||
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
|
# Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+
|
||||||
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
|
# r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK
|
||||||
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
|
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
|
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
|
||||||
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
|
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
|
||||||
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
|
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
|
||||||
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
|
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
|
||||||
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
|
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
|
||||||
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
|
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
|
||||||
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
|
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
|
||||||
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
|
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
|
||||||
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
|
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
|
||||||
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
|
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
|
||||||
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
|
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
|
||||||
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
|
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
|
||||||
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
|
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
|
||||||
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
|
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
|
||||||
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
|
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
|
||||||
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
|
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
|
||||||
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
|
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
|
||||||
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
|
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
|
||||||
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
|
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
|
||||||
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
|
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
|
||||||
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
|
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
|
||||||
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
|
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
|
||||||
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
|
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
|
||||||
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
|
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
|
||||||
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
|
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
|
||||||
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
|
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
|
||||||
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
|
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
|
||||||
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
|
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
|
||||||
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
|
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
|
||||||
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
|
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
|
||||||
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
|
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
|
||||||
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
|
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
|
||||||
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
|
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
|
||||||
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
|
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
|
||||||
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
|
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
|
||||||
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
|
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
|
||||||
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
|
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
|
||||||
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
|
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
|
||||||
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZjCCFWICAQEwgZUwfjELMAkG
|
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFVgwghVUAgEBMIGVMH4x
|
||||||
|
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
|
||||||
|
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
|
||||||
|
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA
|
||||||
|
# AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
|
||||||
|
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFxZ
|
||||||
|
# Yezh3liQqiGQuXNa+zYfoSIbLqOpdEn2ZKskBkisMEIGCisGAQQBgjcCAQwxNDAy
|
||||||
|
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
|
||||||
|
# b20wDQYJKoZIhvcNAQEBBQAEggEAjLUrwCXJCPHZulZuKAQSX+MfnIRFAhlN7ru2
|
||||||
|
# 6H8rudvhkWgqMISkLb9gFDPR5FhR4sqdYgKW4P0ERao9ypCGi1FWDLqygC2XBbHj
|
||||||
|
# NEQHBxHJs5SMsMAXNSIcYHqVAvhF3nXoseaNBkhOTrkQ1FS/fW7AfDGRbsiiESzv
|
||||||
|
# lebf92shZylBFKOsKQLAL0mF/B7xrxHJIj5dgQoD1phATRNHOEQj3jgmkidFWowV
|
||||||
|
# 4r8MzbxRhAEORbnJexlUoDQJQH3YwxuUyXkTvrYMTKSbGJLlwRaZQbrcBU0k4gCH
|
||||||
|
# y8Sci+p9Rq+aOTzLCoNrZyh9E7OdwVDm1FJAtY30bV50T2WSFKGCEuIwghLeBgor
|
||||||
|
# BgEEAYI3AwMBMYISzjCCEsoGCSqGSIb3DQEHAqCCErswghK3AgEDMQ8wDQYJYIZI
|
||||||
|
# AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE
|
||||||
|
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCD7JNcBBSfhlKPL1tN3CEKRKJuT/dZ8RO9K
|
||||||
|
# orYLXJeLTwIGXvN89YD7GBMyMDIwMDcwMTE0MTYyMC40MDVaMASAAgH0oIHQpIHN
|
||||||
|
# MIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQx
|
||||||
|
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
|
||||||
|
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
|
||||||
|
# VFNTIEVTTjoxNzlFLTRCQjAtODI0NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
|
||||||
|
# U3RhbXAgU2VydmljZaCCDjkwggTxMIID2aADAgECAhMzAAABDKp4btzMQkzBAAAA
|
||||||
|
# AAEMMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
|
||||||
|
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
|
||||||
|
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
|
||||||
|
# MB4XDTE5MTAyMzIzMTkxNloXDTIxMDEyMTIzMTkxNlowgcoxCzAJBgNVBAYTAlVT
|
||||||
|
# MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
|
||||||
|
# b2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVy
|
||||||
|
# YXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjE3OUUtNEJC
|
||||||
|
# MC04MjQ2MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIB
|
||||||
|
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq5011+XqVJmQKtiw39igeEMv
|
||||||
|
# CLcZ1forbmxsDkpnCN1SrThKI+n2Pr3zqTzJVgdJFCoKm1ks1gtRJ7HaL6tDkrOw
|
||||||
|
# 8XJmfJaxyQAluCQ+e40NI+A4w+u59Gy89AVY5lJNrmCva6gozfg1kxw6abV5WWr+
|
||||||
|
# PjEpNCshO4hxv3UqgMcCKnT2YVSZzF1Gy7APub1fY0P1vNEuOFKrNCEEvWIKRrqs
|
||||||
|
# eyBB73G8KD2yw6jfz0VKxNSRAdhJV/ghOyrDt5a+L6C3m1rpr8sqiof3iohv3ANI
|
||||||
|
# gNqw6ex+4+G+B7JMbIHbGpPdebedL6ePbuBCnbgJoDn340k0aw6ij21GvvUnkQID
|
||||||
|
# AQABo4IBGzCCARcwHQYDVR0OBBYEFAlCOq9DDIa0A0oqgKtM5vjuZeK+MB8GA1Ud
|
||||||
|
# IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0
|
||||||
|
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0
|
||||||
|
# YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
|
||||||
|
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB
|
||||||
|
# XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH
|
||||||
|
# AwgwDQYJKoZIhvcNAQELBQADggEBAET3xBg/IZ9zdOfwbDGK7cK3qKYt/qUOlbRB
|
||||||
|
# zgeNjb32K86nGeRGkBee10dVOEGWUw6KtBeWh1LQ70b64/tLtiLcsf9JzaAyDYb1
|
||||||
|
# sRmMi5fjRZ753TquaT8V7NJ7RfEuYfvZlubfQD0MVbU4tzsdZdYuxE37V2J9pN89
|
||||||
|
# j7GoFNtAnSnCn1MRxENAILgt9XzeQzTEDhFYW0N2DNphTkRPXGjpDmwi6WtkJ5fv
|
||||||
|
# 0iTyB4dwEC+/ed0lGbFLcytJoMwfTNMdH6gcnHlMzsniornGFZa5PPiV78XoZ9Fe
|
||||||
|
# upKo8ZKNGhLLLB5GTtqfHex5no3ioVSq+NthvhX0I/V+iXJsopowggZxMIIEWaAD
|
||||||
|
# AgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
|
||||||
|
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
|
||||||
|
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBD
|
||||||
|
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3
|
||||||
|
# MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
|
||||||
|
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
|
||||||
|
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkq
|
||||||
|
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWl
|
||||||
|
# CgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/Fg
|
||||||
|
# iIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeR
|
||||||
|
# X4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/Xcf
|
||||||
|
# PfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogI
|
||||||
|
# Neh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB
|
||||||
|
# 5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvF
|
||||||
|
# M2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP
|
||||||
|
# BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjE
|
||||||
|
# MFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv
|
||||||
|
# Y3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEF
|
||||||
|
# BQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
|
||||||
|
# a2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8E
|
||||||
|
# gZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5t
|
||||||
|
# aWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcC
|
||||||
|
# AjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUA
|
||||||
|
# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Pr
|
||||||
|
# psz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOM
|
||||||
|
# zPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCv
|
||||||
|
# OA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v
|
||||||
|
# /rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99
|
||||||
|
# lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1kl
|
||||||
|
# D3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQ
|
||||||
|
# Hm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30
|
||||||
|
# uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp
|
||||||
|
# 25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HS
|
||||||
|
# xVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi6
|
||||||
|
# 2jbb01+P3nSISRKhggLLMIICNAIBATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMx
|
||||||
|
# CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
|
||||||
|
# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
|
||||||
|
# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTc5RS00QkIw
|
||||||
|
# LTgyNDYxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
|
||||||
|
# ATAHBgUrDgMCGgMVAMsg9FQ9pgPLXI2Ld5z7xDS0QAZ9oIGDMIGApH4wfDELMAkG
|
||||||
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
|
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
|
||||||
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
|
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
|
||||||
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
|
# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDipo0MMCIY
|
||||||
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
|
# DzIwMjAwNzAxMTIxODIwWhgPMjAyMDA3MDIxMjE4MjBaMHQwOgYKKwYBBAGEWQoE
|
||||||
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQga11B1DE+
|
# ATEsMCowCgIFAOKmjQwCAQAwBwIBAAICE70wBwIBAAICEeIwCgIFAOKn3owCAQAw
|
||||||
# y9z0lmEO+MC+bhXPKfWALB7Snkn7G/wCUncwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
|
# NgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgC
|
||||||
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
|
# AQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCOPjlHOH8nYtgt2XnpKXenxPUR03ED
|
||||||
# BgkqhkiG9w0BAQEFAASCAQBIgx+sFXkLXf7Xbx7opCD3uhpQGEQ4x/LsqTax0bu1
|
# xPBm8XR5Z1vIq53RU9jG6yYcYNTdK+q38SGZtu0W/SgagTfKCQhjhRakuv7rGSs2
|
||||||
# GC/cxiI+dodUz+T4hKj1ZQyUH0Zlce32GutY048O9tkr7fQyuohoFUgChdIATEOY
|
# dlhx9LGCoc/q1vqmZpRSjkqWVcc/NzmldUWIWnLlV6rmLGoDmfCH5BcsiU6Eo6wU
|
||||||
# qAIESFbDT07i7khJfO2pewlhgM+A5ClvBa8HAvV0wOd+2IVgv3pgow1LEJm0/5NB
|
# iUVwnnXoqsCaBzGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
||||||
# E3IFA+hFrqiWALOY0uUep4H20EHMrbqw3YoV3EodIkTj3fC76q4K/bF84EZLUgjY
|
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
|
||||||
# e4rmXac8n7A9qR18QzGl8usEJej4OHU4nlUT1J734m+AWIFmfb/Zr2MyXED0V4q4
|
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
|
||||||
# Vbmw3O7xD9STeNYrn5RjPmGPEN04akHxhNUSqLIc9vxQoYIS8DCCEuwGCisGAQQB
|
# QSAyMDEwAhMzAAABDKp4btzMQkzBAAAAAAEMMA0GCWCGSAFlAwQCAQUAoIIBSjAa
|
||||||
# gjcDAwExghLcMIIS2AYJKoZIhvcNAQcCoIISyTCCEsUCAQMxDzANBglghkgBZQME
|
# BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIDpwhjyu
|
||||||
# AgEFADCCAVQGCyqGSIb3DQEJEAEEoIIBQwSCAT8wggE7AgEBBgorBgEEAYRZCgMB
|
# zgu3Kmxpnpz86ZlthBqEzG5vaEMOkYRyuFCaMIH6BgsqhkiG9w0BCRACLzGB6jCB
|
||||||
# MDEwDQYJYIZIAWUDBAIBBQAEIPPK1A0D1n7ZEdgTjKPY4sWiOMtohMqGpFvG55NY
|
# 5zCB5DCBvQQgg5AWKX7M1+m2//+V7qmRvt1K/ww5Muu8XzGJBqygVCkwgZgwgYCk
|
||||||
# SFHeAgZepuJh/dEYEjIwMjAwNTI5MTYyNzE1LjMxWjAEgAIB9KCB1KSB0TCBzjEL
|
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
|
||||||
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
|
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
|
||||||
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWlj
|
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAQyqeG7czEJMwQAA
|
||||||
# cm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBU
|
# AAABDDAiBCD11urvv5vgo4gFVQ2NMVrzgxT87Yuiq16YdswYbaYeITANBgkqhkiG
|
||||||
# U1MgRVNOOjYwQkMtRTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T
|
# 9w0BAQsFAASCAQAi3q8hwcT2ft4b2EleaiyZxOImV/cKusmth1dtCh5/Jb0GbOld
|
||||||
# dGFtcCBTZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAEm37pLIrmCggcAAAAA
|
# f5cSalrjf42MNPodWAtgmWozkYrQF6HxnsOiYiamfRA8E3E7xyRMy7AFfAhjcwMi
|
||||||
# ASYwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
|
# xaW4Iye6E1Ec6LtULANxfDtG/KIdCWdZxKqOezL3nzFNQWmm1mXPV+UnKpnJkA3E
|
||||||
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
|
# DsQOUWk8J6ojDurhrP536WI+3arg8PcnppHBLd/xNKYdlsTb+6qndgzKXkDDt1CV
|
||||||
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw
|
# 4zCyuZ7bO8eyZAmNoSZz22k7vus9UjBz/CDhXylo20N43nr29rWPItUgH4uvOGQn
|
||||||
# HhcNMTkxMjE5MDExNDU5WhcNMjEwMzE3MDExNDU5WjCBzjELMAkGA1UEBhMCVVMx
|
# t26Y/yjBaQImz32psrfJEMbQ7cl789s8WOx8
|
||||||
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
|
# SIG # End signature block
|
||||||
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJh
|
|
||||||
# dGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjYwQkMt
|
|
||||||
# RTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
|
|
||||||
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnjC+hpxO8w2VdBO18X8L
|
|
||||||
# Hk6XdfR9yNQ0y+MuBOY7n5YdgkVunvbk/f6q8UoNFAdYQjVLPSAHbi6tUMiNeMGH
|
|
||||||
# k1U0lUxAkja2W2/szj/ghuFklvfHNBbsuiUShlhRlqcFNS7KXL2iwKDijmOhWJPY
|
|
||||||
# a2bLEr4W/mQLbSXail5p6m138Ttx4MAVEzzuGI0Kwr8ofIL7z6zCeWDiBM57LrNC
|
|
||||||
# qHOA2wboeuMsG4O0Oz2LMAzBLbJZPRPnZAD2HdD4HUL2mzZ8wox74Mekb7RzrUP3
|
|
||||||
# hiHpxXZceJvhIEKfAgVkB5kTZQnio8A1JijMjw8f4TmsJPdJWpi8ei73sexe8/Yj
|
|
||||||
# cwIDAQABo4IBGzCCARcwHQYDVR0OBBYEFEmrrB8XsH6YQo3RWKZfxqM0DmFBMB8G
|
|
||||||
# A1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeG
|
|
||||||
# RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rp
|
|
||||||
# bVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH
|
|
||||||
# MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3Rh
|
|
||||||
# UENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB
|
|
||||||
# BQUHAwgwDQYJKoZIhvcNAQELBQADggEBAECW+51o6W/0J/O/npudfjVzMXq0u0cs
|
|
||||||
# HjqXpdRyH6o03jlmY5MXAui3cmPBKufijJxD2pMRPVMUNh3VA0PQuJeYrP06oFdq
|
|
||||||
# LpLxd3IJARm98vzaMgCz2nCwBDpe9X2M3Js9K1GAX+w4Az8N7J+Z6P1OD0VxHBdq
|
|
||||||
# eTaqDN1lk1vwagTN7t/WitxMXRDz0hRdYiWbATBAVgXXCOfzs3hnEv1n/EDab9HX
|
|
||||||
# OLMXKVY/+alqYKdV9lkuRp8Us1Q1WZy9z72Azu9x4mzft3fJ1puTjBHo5tHfixZo
|
|
||||||
# ummbI+WwjVCrku7pskJahfNi5amSgrqgR6nWAwvpJELccpVLdSxxmG0wggZxMIIE
|
|
||||||
# WaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
|
|
||||||
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
|
||||||
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v
|
|
||||||
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0y
|
|
||||||
# NTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
|
|
||||||
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
|
|
||||||
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjAN
|
|
||||||
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RU
|
|
||||||
# ENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBE
|
|
||||||
# D/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50
|
|
||||||
# YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd
|
|
||||||
# /XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaR
|
|
||||||
# togINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB
|
|
||||||
# o4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8
|
|
||||||
# RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB
|
|
||||||
# hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO
|
|
||||||
# mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
|
|
||||||
# a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr
|
|
||||||
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
|
|
||||||
# bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSAB
|
|
||||||
# Af8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3
|
|
||||||
# dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEF
|
|
||||||
# BQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBt
|
|
||||||
# AGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Eh
|
|
||||||
# b7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7
|
|
||||||
# uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqR
|
|
||||||
# UgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9
|
|
||||||
# Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8
|
|
||||||
# +n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+
|
|
||||||
# Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh
|
|
||||||
# 2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRy
|
|
||||||
# zR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoo
|
|
||||||
# uLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx
|
|
||||||
# 16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341
|
|
||||||
# Hgi62jbb01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMC
|
|
||||||
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
|
|
||||||
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9w
|
|
||||||
# ZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjYw
|
|
||||||
# QkMtRTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
|
|
||||||
# aWNloiMKAQEwBwYFKw4DAhoDFQAKZzI5aZnESumrToHx3Lqgxnr//KCBgzCBgKR+
|
|
||||||
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
|
|
||||||
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
|
|
||||||
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA
|
|
||||||
# 4nuQTDAiGA8yMDIwMDUyOTE3NDQ0NFoYDzIwMjAwNTMwMTc0NDQ0WjB3MD0GCisG
|
|
||||||
# AQQBhFkKBAExLzAtMAoCBQDie5BMAgEAMAoCAQACAiZJAgH/MAcCAQACAhEjMAoC
|
|
||||||
# BQDifOHMAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA
|
|
||||||
# AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAprmyJTXdH9FmQZ0I
|
|
||||||
# mRSJdjc/RrSqDm8DUEq/h3FL73G/xvg9MbQj1J/h3hdlSIPcQXjrhL8hud/vyF0j
|
|
||||||
# IFaTK5YOcixkX++9t7Vz3Mn0KkQo8F4DNSyZEPpz682AyKKwLMJDy52pFFFKNP5l
|
|
||||||
# NpOz6YY1Od1xvk4nyN1WwfLnGswxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJV
|
|
||||||
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
|
|
||||||
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
|
|
||||||
# ZS1TdGFtcCBQQ0EgMjAxMAITMwAAASbfuksiuYKCBwAAAAABJjANBglghkgBZQME
|
|
||||||
# AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ
|
|
||||||
# BDEiBCB0IE0Q6P23RQlh8TFyp57UQQUF/sbui7mOMStRgTFZxTCB+gYLKoZIhvcN
|
|
||||||
# AQkQAi8xgeowgecwgeQwgb0EIDb9z++evV5wDO9qk5ZnbEZ8CTOuR+kZyu8xbTsJ
|
|
||||||
# CXUPMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
|
|
||||||
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
|
|
||||||
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEm
|
|
||||||
# 37pLIrmCggcAAAAAASYwIgQgtwi02bvsGAOdpAxEF607G6g9PlyS8vc2bAUSHovH
|
|
||||||
# /IIwDQYJKoZIhvcNAQELBQAEggEAEMCfsXNudrjztjI6JNyNDVpdF1axRVcGiNy6
|
|
||||||
# 67pgb1EePsjA2EaBB+5ZjgO/73JxuiVgsoXgH7em8tKG5RQJtcm5obVDb+jKksK4
|
|
||||||
# qcFLA1f7seQRGfE06UAPnSFh2GqMtTNJGCXWwqWLH2LduTjOqPt8Nupo16ABFIT2
|
|
||||||
# akTzBSJ81EHBkEU0Et6CgeaZiBYrCCXUtD+ASvLDkPSrjweQGu3Zk1SSROEzxMY9
|
|
||||||
# jdlGfMkK2krMd9ub9UZ13RcQDijJqo+h6mz76pAuiFFvuQl6wMoSGFaaUQwfd+WQ
|
|
||||||
# gXlVVX/A9JFBihrxnDVglEPlsIOxCHkTeIxLfnAkCbax+9pevA==
|
|
||||||
# SIG # End signature block
|
|
||||||
18
src/Misc/download-runner.sh
Executable file
18
src/Misc/download-runner.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# if the scope has a slash, it's a repo runner
|
||||||
|
# orgs_or_repos="orgs"
|
||||||
|
# if [[ "$GITHUB_RUNNER_SCOPE" == *\/* ]]; then
|
||||||
|
# orgs_or_repos="repos"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
#RUNNER_DOWNLOAD_URL=$(curl -s -X GET ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/downloads -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json" | jq -r '.[]|select(.os=="linux" and .architecture=="x64")|.download_url')
|
||||||
|
|
||||||
|
# download actions and unzip it
|
||||||
|
#curl -Ls ${RUNNER_DOWNLOAD_URL} | tar xz \
|
||||||
|
|
||||||
|
curl -Ls https://github.com/TingluoHuang/runner/releases/download/test/actions-runner-linux-x64-2.299.0.tar.gz | tar xz
|
||||||
|
|
||||||
|
# delete the download tar.gz file
|
||||||
|
rm -f ${RUNNER_DOWNLOAD_URL##*/}
|
||||||
77
src/Misc/entrypoint.sh
Executable file
77
src/Misc/entrypoint.sh
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
function fatal() {
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "${GITHUB_PAT:-""}" ] || fatal "GITHUB_PAT variable must be set"
|
||||||
|
[ -n "${RUNNER_CONFIG_URL:-""}" ] || fatal "RUNNER_CONFIG_URL variable must be set"
|
||||||
|
[ -n "${RUNNER_NAME:-""}" ] || fatal "RUNNER_NAME variable must be set"
|
||||||
|
|
||||||
|
# if [ -n "${RUNNER_NAME}" ]; then
|
||||||
|
# # Use container id to gen unique runner name if name not provide
|
||||||
|
# CONTAINER_ID=$(cat /proc/self/cgroup | head -n 1 | tr '/' '\n' | tail -1 | cut -c1-12)
|
||||||
|
# RUNNER_NAME="actions-runner-${CONTAINER_ID}"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# if the scope has a slash, it's a repo runner
|
||||||
|
# orgs_or_repos="orgs"
|
||||||
|
# if [[ "$GITHUB_RUNNER_SCOPE" == *\/* ]]; then
|
||||||
|
# orgs_or_repos="repos"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# RUNNER_REG_URL="${GITHUB_SERVER_URL:=https://github.com}/${GITHUB_RUNNER_SCOPE}"
|
||||||
|
|
||||||
|
echo "Runner Name : ${RUNNER_NAME}"
|
||||||
|
echo "Registration URL : ${RUNNER_CONFIG_URL}"
|
||||||
|
# echo "GitHub API URL : ${GITHUB_API_URL:=https://api.github.com}"
|
||||||
|
echo "Runner Labels : ${RUNNER_LABELS:=""}"
|
||||||
|
|
||||||
|
# TODO: if api url is not default, validate it ends in /api/v3
|
||||||
|
|
||||||
|
RUNNER_LABELS_ARG=""
|
||||||
|
if [ -n "${RUNNER_LABELS}" ]; then
|
||||||
|
RUNNER_LABELS_ARG="--labels ${RUNNER_LABELS}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
RUNNER_GROUP_ARG=""
|
||||||
|
if [ -n "${RUNNER_GROUP}" ]; then
|
||||||
|
RUNNER_GROUP_ARG="--runnergroup ${RUNNER_GROUP}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if [ -n "${K8S_HOST_IP}" ]; then
|
||||||
|
# export http_proxy=http://$K8S_HOST_IP:9090
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# curl -v -s -X POST ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/registration-token -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json"
|
||||||
|
|
||||||
|
# Generate registration token
|
||||||
|
# RUNNER_REG_TOKEN=$(curl -s -X POST ${GITHUB_API_URL}/${orgs_or_repos}/${GITHUB_RUNNER_SCOPE}/actions/runners/registration-token -H "authorization: token $GITHUB_PAT" -H "accept: application/vnd.github.everest-preview+json" | jq -r '.token')
|
||||||
|
|
||||||
|
# Create the runner and configure it
|
||||||
|
./config.sh --unattended --name $RUNNER_NAME --url $RUNNER_CONFIG_URL --pat $GITHUB_PAT $RUNNER_LABELS_ARG $RUNNER_GROUP_ARG --replace --ephemeral
|
||||||
|
|
||||||
|
# while (! docker version ); do
|
||||||
|
# # Docker takes a few seconds to initialize
|
||||||
|
# echo "Waiting for Docker to launch..."
|
||||||
|
# sleep 1
|
||||||
|
# done
|
||||||
|
|
||||||
|
# unset env
|
||||||
|
unset RUNNER_CONFIG_URL
|
||||||
|
unset GITHUB_PAT
|
||||||
|
unset RUNNER_NAME
|
||||||
|
unset RUNNER_GROUP
|
||||||
|
unset RUNNER_LABELS
|
||||||
|
unset RUNNER_LABELS_ARG
|
||||||
|
unset RUNNER_GROUP_ARG
|
||||||
|
|
||||||
|
# Run it
|
||||||
|
./bin/runsvc.sh interactive
|
||||||
|
|
||||||
|
# export http_proxy=""
|
||||||
|
# dockerdpid=$(kubectl exec $K8S_POD_NAME --container docker-host -- pidof dockerd)
|
||||||
|
# kubectl exec $K8S_POD_NAME --container docker-host -- kill -SIGINT $dockerdpid
|
||||||
@@ -1683,9 +1683,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.15",
|
"version": "4.17.19",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lodash.unescape": {
|
"lodash.unescape": {
|
||||||
|
|||||||
25
src/Misc/jobcomplete.sh
Executable file
25
src/Misc/jobcomplete.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Test-0"
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Test-1"
|
||||||
|
function fatal() {
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Test-2"
|
||||||
|
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||||
|
|
||||||
|
echo "Test-3"
|
||||||
|
# echo $http_proxy
|
||||||
|
# unset http_proxy
|
||||||
|
# unset https_proxy
|
||||||
|
# export http_proxy=
|
||||||
|
# export HTTP_PROXY=
|
||||||
|
|
||||||
|
echo "Test-4"
|
||||||
|
kubectl annotate pods $K8S_POD_NAME JOBCOMPLETE=$(date +%s) || fatal "Can't annotate job complete"
|
||||||
|
|
||||||
|
echo "Test-5"
|
||||||
|
exit 0
|
||||||
25
src/Misc/jobrunning.sh
Executable file
25
src/Misc/jobrunning.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Test-0"
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Test-1"
|
||||||
|
function fatal() {
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Test-2"
|
||||||
|
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||||
|
|
||||||
|
echo "Test-3"
|
||||||
|
# echo $http_proxy
|
||||||
|
# unset http_proxy
|
||||||
|
# unset https_proxy
|
||||||
|
# export http_proxy=
|
||||||
|
# export HTTP_PROXY=
|
||||||
|
|
||||||
|
echo "Test-4"
|
||||||
|
kubectl annotate pods $K8S_POD_NAME JOBRUNNING=$(date +%s) --overwrite || fatal "Can't annotate job running"
|
||||||
|
|
||||||
|
echo "Test-5"
|
||||||
|
exit 0
|
||||||
32
src/Misc/jobstart.sh
Executable file
32
src/Misc/jobstart.sh
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Test-0"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "Test-1"
|
||||||
|
function fatal() {
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Test-2"
|
||||||
|
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||||
|
|
||||||
|
echo "Test-3"
|
||||||
|
# echo $http_proxy
|
||||||
|
# # unset http_proxy
|
||||||
|
# # unset https_proxy
|
||||||
|
# export http_proxy=
|
||||||
|
# export HTTP_PROXY=
|
||||||
|
|
||||||
|
echo "Test-4"
|
||||||
|
kubectl -v9 get pod
|
||||||
|
|
||||||
|
echo "Test-5"
|
||||||
|
echo $K8S_POD_NAME
|
||||||
|
timestamp=$(date +%s)
|
||||||
|
echo $timestamp
|
||||||
|
|
||||||
|
kubectl annotate pods $K8S_POD_NAME JOBSTART=$timestamp
|
||||||
|
|
||||||
|
echo "Test-5"
|
||||||
@@ -67,7 +67,7 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
|
|||||||
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
done
|
done
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
cd $DIR
|
cd "$DIR"
|
||||||
|
|
||||||
source ./env.sh
|
source ./env.sh
|
||||||
|
|
||||||
|
|||||||
23
src/Misc/runner_lifecycle.sh
Executable file
23
src/Misc/runner_lifecycle.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
EVENT=$1
|
||||||
|
TIMESTAMP=$2
|
||||||
|
|
||||||
|
echo $EVENT
|
||||||
|
echo $TIMESTAMP
|
||||||
|
|
||||||
|
function fatal() {
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "${K8S_POD_NAME:-""}" ] || fatal "K8S_POD_NAME variable must be set"
|
||||||
|
echo $K8S_POD_NAME
|
||||||
|
|
||||||
|
kubectl get pod
|
||||||
|
|
||||||
|
kubectl annotate pods $K8S_POD_NAME $EVENT=$TIMESTAMP
|
||||||
|
|
||||||
|
echo "DONE"
|
||||||
@@ -33,6 +33,9 @@ namespace GitHub.Runner.Common
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string PoolName { get; set; }
|
public string PoolName { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public bool Ephemeral { get; set; }
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string ServerUrl { get; set; }
|
public string ServerUrl { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -99,9 +99,11 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
// Secret args. Must be added to the "Secrets" getter as well.
|
// Secret args. Must be added to the "Secrets" getter as well.
|
||||||
public static readonly string Token = "token";
|
public static readonly string Token = "token";
|
||||||
|
public static readonly string PAT = "pat";
|
||||||
public static readonly string WindowsLogonPassword = "windowslogonpassword";
|
public static readonly string WindowsLogonPassword = "windowslogonpassword";
|
||||||
public static string[] Secrets => new[]
|
public static string[] Secrets => new[]
|
||||||
{
|
{
|
||||||
|
PAT,
|
||||||
Token,
|
Token,
|
||||||
WindowsLogonPassword,
|
WindowsLogonPassword,
|
||||||
};
|
};
|
||||||
@@ -120,9 +122,9 @@ namespace GitHub.Runner.Common
|
|||||||
public static class Flags
|
public static class Flags
|
||||||
{
|
{
|
||||||
public static readonly string Commit = "commit";
|
public static readonly string Commit = "commit";
|
||||||
|
public static readonly string Ephemeral = "ephemeral";
|
||||||
public static readonly string Help = "help";
|
public static readonly string Help = "help";
|
||||||
public static readonly string Replace = "replace";
|
public static readonly string Replace = "replace";
|
||||||
public static readonly string Once = "once";
|
|
||||||
public static readonly string RunAsService = "runasservice";
|
public static readonly string RunAsService = "runasservice";
|
||||||
public static readonly string Unattended = "unattended";
|
public static readonly string Unattended = "unattended";
|
||||||
public static readonly string Version = "version";
|
public static readonly string Version = "version";
|
||||||
|
|||||||
51
src/Runner.Common/Util/EncodingUtil.cs
Normal file
51
src/Runner.Common/Util/EncodingUtil.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Runner.Common;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Common.Util
|
||||||
|
{
|
||||||
|
public static class EncodingUtil
|
||||||
|
{
|
||||||
|
public static async Task SetEncoding(IHostContext hostContext, Tracing trace, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
#if OS_WINDOWS
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Console.InputEncoding.CodePage != 65001)
|
||||||
|
{
|
||||||
|
using (var p = hostContext.CreateService<IProcessInvoker>())
|
||||||
|
{
|
||||||
|
// Use UTF8 code page
|
||||||
|
int exitCode = await p.ExecuteAsync(workingDirectory: hostContext.GetDirectory(WellKnownDirectory.Work),
|
||||||
|
fileName: WhichUtil.Which("chcp", true, trace),
|
||||||
|
arguments: "65001",
|
||||||
|
environment: null,
|
||||||
|
requireExitCodeZero: false,
|
||||||
|
outputEncoding: null,
|
||||||
|
killProcessOnCancel: false,
|
||||||
|
redirectStandardIn: null,
|
||||||
|
inheritConsoleHandler: true,
|
||||||
|
cancellationToken: cancellationToken);
|
||||||
|
if (exitCode == 0)
|
||||||
|
{
|
||||||
|
trace.Info("Successfully returned to code page 65001 (UTF8)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace.Warning($"'chcp 65001' failed with exit code {exitCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
trace.Warning($"'chcp 65001' failed with exception {ex.Message}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Dummy variable to prevent compiler error CS1998: "This async method lacks 'await' operators and will run synchronously..."
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,10 +28,10 @@ namespace GitHub.Runner.Listener
|
|||||||
private readonly string[] validFlags =
|
private readonly string[] validFlags =
|
||||||
{
|
{
|
||||||
Constants.Runner.CommandLine.Flags.Commit,
|
Constants.Runner.CommandLine.Flags.Commit,
|
||||||
|
Constants.Runner.CommandLine.Flags.Ephemeral,
|
||||||
Constants.Runner.CommandLine.Flags.Help,
|
Constants.Runner.CommandLine.Flags.Help,
|
||||||
Constants.Runner.CommandLine.Flags.Replace,
|
Constants.Runner.CommandLine.Flags.Replace,
|
||||||
Constants.Runner.CommandLine.Flags.RunAsService,
|
Constants.Runner.CommandLine.Flags.RunAsService,
|
||||||
Constants.Runner.CommandLine.Flags.Once,
|
|
||||||
Constants.Runner.CommandLine.Flags.Unattended,
|
Constants.Runner.CommandLine.Flags.Unattended,
|
||||||
Constants.Runner.CommandLine.Flags.Version
|
Constants.Runner.CommandLine.Flags.Version
|
||||||
};
|
};
|
||||||
@@ -42,6 +42,7 @@ namespace GitHub.Runner.Listener
|
|||||||
Constants.Runner.CommandLine.Args.Labels,
|
Constants.Runner.CommandLine.Args.Labels,
|
||||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||||
Constants.Runner.CommandLine.Args.Name,
|
Constants.Runner.CommandLine.Args.Name,
|
||||||
|
Constants.Runner.CommandLine.Args.PAT,
|
||||||
Constants.Runner.CommandLine.Args.Pool,
|
Constants.Runner.CommandLine.Args.Pool,
|
||||||
Constants.Runner.CommandLine.Args.StartupType,
|
Constants.Runner.CommandLine.Args.StartupType,
|
||||||
Constants.Runner.CommandLine.Args.Token,
|
Constants.Runner.CommandLine.Args.Token,
|
||||||
@@ -63,8 +64,7 @@ namespace GitHub.Runner.Listener
|
|||||||
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
||||||
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
||||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||||
|
public bool Ephemeral => TestFlag(Constants.Runner.CommandLine.Flags.Ephemeral);
|
||||||
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
|
||||||
|
|
||||||
// Constructor.
|
// Constructor.
|
||||||
public CommandSettings(IHostContext context, string[] args)
|
public CommandSettings(IHostContext context, string[] args)
|
||||||
@@ -178,6 +178,11 @@ namespace GitHub.Runner.Listener
|
|||||||
validator: Validators.NonEmptyValidator);
|
validator: Validators.NonEmptyValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetGitHubPersonalAccessToken()
|
||||||
|
{
|
||||||
|
return GetArg(name: Constants.Runner.CommandLine.Args.PAT);
|
||||||
|
}
|
||||||
|
|
||||||
public string GetRunnerRegisterToken()
|
public string GetRunnerRegisterToken()
|
||||||
{
|
{
|
||||||
return GetArgOrPrompt(
|
return GetArgOrPrompt(
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ using GitHub.Services.OAuth;
|
|||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace GitHub.Runner.Listener.Configuration
|
namespace GitHub.Runner.Listener.Configuration
|
||||||
@@ -107,8 +109,21 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
runnerSettings.GitHubUrl = inputUrl;
|
runnerSettings.GitHubUrl = inputUrl;
|
||||||
var githubToken = command.GetRunnerRegisterToken();
|
var githubPAT = command.GetGitHubPersonalAccessToken();
|
||||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, githubToken, Constants.RunnerEvent.Register);
|
var registerToken = string.Empty;
|
||||||
|
if (!string.IsNullOrEmpty(githubPAT))
|
||||||
|
{
|
||||||
|
Trace.Info("Retriving runner register token using GitHub PAT.");
|
||||||
|
var jitToken = await GetJITRunnerTokenAsync(inputUrl, githubPAT, "registration");
|
||||||
|
Trace.Info($"Retrived runner register token is good to {jitToken.ExpiresAt}.");
|
||||||
|
HostContext.SecretMasker.AddValue(jitToken.Token);
|
||||||
|
registerToken = jitToken.Token;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(registerToken))
|
||||||
|
{
|
||||||
|
registerToken = command.GetRunnerRegisterToken();
|
||||||
|
}
|
||||||
|
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
||||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
runnerSettings.ServerUrl = authResult.TenantUrl;
|
||||||
creds = authResult.ToVssCredentials();
|
creds = authResult.ToVssCredentials();
|
||||||
Trace.Info("cred retrieved via GitHub auth");
|
Trace.Info("cred retrieved via GitHub auth");
|
||||||
@@ -177,6 +192,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
TaskAgent agent;
|
TaskAgent agent;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
runnerSettings.Ephemeral = command.Ephemeral;
|
||||||
runnerSettings.AgentName = command.GetRunnerName();
|
runnerSettings.AgentName = command.GetRunnerName();
|
||||||
|
|
||||||
_term.WriteLine();
|
_term.WriteLine();
|
||||||
@@ -193,7 +209,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
if (command.GetReplace())
|
if (command.GetReplace())
|
||||||
{
|
{
|
||||||
// Update existing agent with new PublicKey, agent version.
|
// Update existing agent with new PublicKey, agent version.
|
||||||
agent = UpdateExistingAgent(agent, publicKey, userLabels);
|
agent = UpdateExistingAgent(agent, publicKey, userLabels, runnerSettings.Ephemeral);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -216,7 +232,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Create a new agent.
|
// Create a new agent.
|
||||||
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels);
|
agent = CreateNewAgent(runnerSettings.AgentName, publicKey, userLabels, runnerSettings.Ephemeral);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -356,8 +372,22 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var githubToken = command.GetRunnerDeletionToken();
|
var githubPAT = command.GetGitHubPersonalAccessToken();
|
||||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, githubToken, Constants.RunnerEvent.Remove);
|
var deletionToken = string.Empty;
|
||||||
|
if (!string.IsNullOrEmpty(githubPAT))
|
||||||
|
{
|
||||||
|
Trace.Info("Retriving runner deletion token using GitHub PAT.");
|
||||||
|
var jitToken = await GetJITRunnerTokenAsync(settings.GitHubUrl, githubPAT, "remove");
|
||||||
|
Trace.Info($"Retrived runner deletion token is good to {jitToken.ExpiresAt}.");
|
||||||
|
HostContext.SecretMasker.AddValue(jitToken.Token);
|
||||||
|
deletionToken = jitToken.Token;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(deletionToken))
|
||||||
|
{
|
||||||
|
deletionToken = command.GetRunnerDeletionToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
|
||||||
creds = authResult.ToVssCredentials();
|
creds = authResult.ToVssCredentials();
|
||||||
Trace.Info("cred retrieved via GitHub auth");
|
Trace.Info("cred retrieved via GitHub auth");
|
||||||
}
|
}
|
||||||
@@ -440,7 +470,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels)
|
private TaskAgent UpdateExistingAgent(TaskAgent agent, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral)
|
||||||
{
|
{
|
||||||
ArgUtil.NotNull(agent, nameof(agent));
|
ArgUtil.NotNull(agent, nameof(agent));
|
||||||
agent.Authorization = new TaskAgentAuthorization
|
agent.Authorization = new TaskAgentAuthorization
|
||||||
@@ -451,6 +481,8 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
// update should replace the existing labels
|
// update should replace the existing labels
|
||||||
agent.Version = BuildConstants.RunnerPackage.Version;
|
agent.Version = BuildConstants.RunnerPackage.Version;
|
||||||
agent.OSDescription = RuntimeInformation.OSDescription;
|
agent.OSDescription = RuntimeInformation.OSDescription;
|
||||||
|
agent.Ephemeral = ephemeral;
|
||||||
|
agent.MaxParallelism = 1;
|
||||||
|
|
||||||
agent.Labels.Clear();
|
agent.Labels.Clear();
|
||||||
|
|
||||||
@@ -466,7 +498,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
return agent;
|
return agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels)
|
private TaskAgent CreateNewAgent(string agentName, RSAParameters publicKey, ISet<string> userLabels, bool ephemeral)
|
||||||
{
|
{
|
||||||
TaskAgent agent = new TaskAgent(agentName)
|
TaskAgent agent = new TaskAgent(agentName)
|
||||||
{
|
{
|
||||||
@@ -477,6 +509,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
MaxParallelism = 1,
|
MaxParallelism = 1,
|
||||||
Version = BuildConstants.RunnerPackage.Version,
|
Version = BuildConstants.RunnerPackage.Version,
|
||||||
OSDescription = RuntimeInformation.OSDescription,
|
OSDescription = RuntimeInformation.OSDescription,
|
||||||
|
Ephemeral = ephemeral,
|
||||||
};
|
};
|
||||||
|
|
||||||
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
agent.Labels.Add(new AgentLabel("self-hosted", LabelType.System));
|
||||||
@@ -498,6 +531,72 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
|
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<GitHubRunnerRegisterToken> GetJITRunnerTokenAsync(string githubUrl, string githubToken, string tokenType)
|
||||||
|
{
|
||||||
|
var githubApiUrl = "";
|
||||||
|
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||||
|
var path = gitHubUrlBuilder.Path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (path.Length == 1)
|
||||||
|
{
|
||||||
|
if (IsHostedServer(gitHubUrlBuilder))
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions/runners/{tokenType}-token";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners/{tokenType}-token";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.Length == 2)
|
||||||
|
{
|
||||||
|
var repoScope = "repos/";
|
||||||
|
if (string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
repoScope = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsHostedServer(gitHubUrlBuilder))
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{repoScope}{path[0]}/{path[1]}/actions/runners/{tokenType}-token";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"'{githubUrl}' should point to an org or repository.");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||||
|
using (var httpClient = new HttpClient(httpClientHandler))
|
||||||
|
{
|
||||||
|
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"github:{githubToken}"));
|
||||||
|
HostContext.SecretMasker.AddValue(base64EncodingToken);
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", base64EncodingToken);
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
|
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github.v3+json");
|
||||||
|
|
||||||
|
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(string.Empty));
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||||
|
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
return StringUtil.ConvertFromJson<GitHubRunnerRegisterToken>(jsonResponse);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||||
|
var errorResponse = await response.Content.ReadAsStringAsync();
|
||||||
|
_term.WriteError(errorResponse);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
|
private async Task<GitHubAuthResult> GetTenantCredential(string githubUrl, string githubToken, string runnerEvent)
|
||||||
{
|
{
|
||||||
var githubApiUrl = "";
|
var githubApiUrl = "";
|
||||||
|
|||||||
@@ -71,6 +71,16 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
public sealed class GitHubRunnerRegisterToken
|
||||||
|
{
|
||||||
|
[DataMember(Name = "token")]
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "expires_at")]
|
||||||
|
public string ExpiresAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public sealed class GitHubAuthResult
|
public sealed class GitHubAuthResult
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -477,6 +477,53 @@ namespace GitHub.Runner.Listener
|
|||||||
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
||||||
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
||||||
|
var jobStartNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||||
|
if (!string.IsNullOrEmpty(jobStartNotification))
|
||||||
|
{
|
||||||
|
term.WriteLine($"{DateTime.UtcNow:u}: Publish JobStart to {jobStartNotification}");
|
||||||
|
using (var jobStartInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||||
|
{
|
||||||
|
jobStartInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stdout.Data))
|
||||||
|
{
|
||||||
|
Trace.Info($"JobStartNotification: {stdout.Data}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jobStartInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
Trace.Error($"JobStartNotification: {stderr.Data}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await jobStartInvoker.ExecuteAsync(
|
||||||
|
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||||
|
fileName: WhichUtil.Which("bash"),
|
||||||
|
arguments: $"-c \"{jobStartNotification} JOBSTART {DateTime.UtcNow.ToString("O")}\"",
|
||||||
|
environment: null,
|
||||||
|
requireExitCodeZero: true,
|
||||||
|
outputEncoding: null,
|
||||||
|
killProcessOnCancel: true,
|
||||||
|
redirectStandardIn: null,
|
||||||
|
inheritConsoleHandler: false,
|
||||||
|
keepStandardInOpen: false,
|
||||||
|
highPriorityProcess: true,
|
||||||
|
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Fail to publish JobStart notification: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
|
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
|
||||||
|
|
||||||
@@ -613,6 +660,53 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
// This should be the last thing to run so we don't notify external parties until actually finished
|
// This should be the last thing to run so we don't notify external parties until actually finished
|
||||||
await notification.JobCompleted(message.JobId);
|
await notification.JobCompleted(message.JobId);
|
||||||
|
var jobCompleteNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||||
|
if (!string.IsNullOrEmpty(jobCompleteNotification))
|
||||||
|
{
|
||||||
|
term.WriteLine($"{DateTime.UtcNow:u}: Publish JobComplete to {jobCompleteNotification}");
|
||||||
|
using (var jobCompleteInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||||
|
{
|
||||||
|
jobCompleteInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stdout.Data))
|
||||||
|
{
|
||||||
|
Trace.Info($"jobCompleteNotification: {stdout.Data}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jobCompleteInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
Trace.Error($"jobCompleteNotification: {stderr.Data}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await jobCompleteInvoker.ExecuteAsync(
|
||||||
|
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||||
|
fileName: WhichUtil.Which("bash"),
|
||||||
|
arguments: $"-c \"{jobCompleteNotification} JOBCOMPLETE {DateTime.UtcNow.ToString("O")}\"",
|
||||||
|
environment: null,
|
||||||
|
requireExitCodeZero: true,
|
||||||
|
outputEncoding: null,
|
||||||
|
killProcessOnCancel: true,
|
||||||
|
redirectStandardIn: null,
|
||||||
|
inheritConsoleHandler: false,
|
||||||
|
keepStandardInOpen: false,
|
||||||
|
highPriorityProcess: true,
|
||||||
|
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Fail to publish JobComplete notification: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,7 +739,56 @@ namespace GitHub.Runner.Listener
|
|||||||
// fire first renew succeed event.
|
// fire first renew succeed event.
|
||||||
firstJobRequestRenewed.TrySetResult(0);
|
firstJobRequestRenewed.TrySetResult(0);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var jobRunningNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||||
|
if (!string.IsNullOrEmpty(jobRunningNotification))
|
||||||
|
{
|
||||||
|
HostContext.GetService<ITerminal>().WriteLine($"{DateTime.UtcNow:u}: Publish JobRunning to {jobRunningNotification}");
|
||||||
|
using (var jobRunningInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||||
|
{
|
||||||
|
jobRunningInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stdout.Data))
|
||||||
|
{
|
||||||
|
Trace.Info($"JobRunningNotification: {stdout.Data}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jobRunningInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
Trace.Error($"JobRunningNotification: {stderr.Data}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await jobRunningInvoker.ExecuteAsync(
|
||||||
|
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||||
|
fileName: WhichUtil.Which("bash"),
|
||||||
|
arguments: $"-c \"{jobRunningNotification} JOBRUNNING {DateTime.UtcNow.ToString("O")}\"",
|
||||||
|
environment: null,
|
||||||
|
requireExitCodeZero: true,
|
||||||
|
outputEncoding: null,
|
||||||
|
killProcessOnCancel: true,
|
||||||
|
redirectStandardIn: null,
|
||||||
|
inheritConsoleHandler: false,
|
||||||
|
keepStandardInOpen: false,
|
||||||
|
highPriorityProcess: true,
|
||||||
|
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Fail to publish JobRunning notification: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (encounteringError > 0)
|
if (encounteringError > 0)
|
||||||
{
|
{
|
||||||
encounteringError = 0;
|
encounteringError = 0;
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ namespace GitHub.Runner.Listener
|
|||||||
HostContext.StartupType = startType;
|
HostContext.StartupType = startType;
|
||||||
|
|
||||||
// Run the runner interactively or as service
|
// Run the runner interactively or as service
|
||||||
return await RunAsync(settings, command.RunOnce);
|
return await RunAsync(settings, settings.Ephemeral);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -474,7 +474,7 @@ Config Options:
|
|||||||
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");
|
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");
|
||||||
_term.WriteLine($@" --windowslogonpassword string Password for the service account. Requires runasservice");
|
_term.WriteLine($@" --windowslogonpassword string Password for the service account. Requires runasservice");
|
||||||
#endif
|
#endif
|
||||||
_term.WriteLine($@"
|
_term.WriteLine($@"
|
||||||
Examples:
|
Examples:
|
||||||
Configure a runner non-interactively:
|
Configure a runner non-interactively:
|
||||||
.{separator}config.{ext} --unattended --url <url> --token <token>
|
.{separator}config.{ext} --unattended --url <url> --token <token>
|
||||||
|
|||||||
@@ -59,6 +59,53 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
Trace.Info($"An update is available.");
|
Trace.Info($"An update is available.");
|
||||||
|
|
||||||
|
var runnerUpdateNotification = Environment.GetEnvironmentVariable("_INTERNAL_RUNNER_LIFECYCLE_NOTIFICATION");
|
||||||
|
if (!string.IsNullOrEmpty(runnerUpdateNotification))
|
||||||
|
{
|
||||||
|
HostContext.GetService<ITerminal>().WriteLine($"{DateTime.UtcNow:u}: Publish RunnerUpdate to {runnerUpdateNotification}");
|
||||||
|
using (var runnerUpdateInvoker = HostContext.CreateService<IProcessInvoker>())
|
||||||
|
{
|
||||||
|
runnerUpdateInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stdout.Data))
|
||||||
|
{
|
||||||
|
Trace.Info($"RunnerUpdateNotification: {stdout.Data}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runnerUpdateInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(stderr.Data))
|
||||||
|
{
|
||||||
|
Trace.Error($"RunnerUpdateNotification: {stderr.Data}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await runnerUpdateInvoker.ExecuteAsync(
|
||||||
|
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
||||||
|
fileName: WhichUtil.Which("bash"),
|
||||||
|
arguments: $"-c \"{runnerUpdateNotification} RUNNERUPDATE {DateTime.UtcNow.ToString("O")}\"",
|
||||||
|
environment: null,
|
||||||
|
requireExitCodeZero: true,
|
||||||
|
outputEncoding: null,
|
||||||
|
killProcessOnCancel: true,
|
||||||
|
redirectStandardIn: null,
|
||||||
|
inheritConsoleHandler: false,
|
||||||
|
keepStandardInOpen: false,
|
||||||
|
highPriorityProcess: true,
|
||||||
|
cancellationToken: new CancellationTokenSource(10000).Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Fail to publish RunnerUpdate notification: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Print console line that warn user not shutdown runner.
|
// Print console line that warn user not shutdown runner.
|
||||||
await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
|
await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
|
||||||
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");
|
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace GitHub.Runner.Sdk
|
|||||||
//
|
//
|
||||||
// For example, on an en-US box, this is required for loading the encoding for the
|
// For example, on an en-US box, this is required for loading the encoding for the
|
||||||
// default console output code page '437'. Without loading the correct encoding for
|
// default console output code page '437'. Without loading the correct encoding for
|
||||||
// code page IBM437, some characters cannot be translated correctly, e.g. write 'ç'
|
// code page IBM437, some characters cannot be translated correctly, e.g. write 'ç'
|
||||||
// from powershell.exe.
|
// from powershell.exe.
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ namespace GitHub.Runner.Worker
|
|||||||
throw new Exception("Required field 'name' is missing in ##[set-env] command.");
|
throw new Exception("Required field 'name' is missing in ##[set-env] command.");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.EnvironmentVariables[envName] = command.Data;
|
context.Global.EnvironmentVariables[envName] = command.Data;
|
||||||
context.SetEnvContext(envName, command.Data);
|
context.SetEnvContext(envName, command.Data);
|
||||||
context.Debug($"{envName}='{command.Data}'");
|
context.Debug($"{envName}='{command.Data}'");
|
||||||
}
|
}
|
||||||
@@ -283,8 +283,8 @@ namespace GitHub.Runner.Worker
|
|||||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||||
{
|
{
|
||||||
ArgUtil.NotNullOrEmpty(command.Data, "path");
|
ArgUtil.NotNullOrEmpty(command.Data, "path");
|
||||||
context.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture));
|
context.Global.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture));
|
||||||
context.PrependPath.Add(command.Data);
|
context.Global.PrependPath.Add(command.Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
||||||
// Log even if we aren't using it to ensure users know.
|
// Log even if we aren't using it to ensure users know.
|
||||||
if (!string.IsNullOrEmpty(executionContext.Variables.Get("PREVIEW_ACTION_TOKEN")))
|
if (!string.IsNullOrEmpty(executionContext.Global.Variables.Get("PREVIEW_ACTION_TOKEN")))
|
||||||
{
|
{
|
||||||
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is deprecated. Please remove it from the repository's secrets");
|
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is deprecated. Please remove it from the repository's secrets");
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ namespace GitHub.Runner.Worker
|
|||||||
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
||||||
|
|
||||||
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed
|
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed
|
||||||
var newActionMetadata = executionContext.Variables.GetBoolean("DistributedTask.NewActionMetadata") ?? false;
|
var newActionMetadata = executionContext.Global.Variables.GetBoolean("DistributedTask.NewActionMetadata") ?? false;
|
||||||
|
|
||||||
var repositoryActions = new List<Pipelines.ActionStep>();
|
var repositoryActions = new List<Pipelines.ActionStep>();
|
||||||
|
|
||||||
@@ -395,6 +395,14 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||||
|
{
|
||||||
|
var compositeAction = definition.Data.Execution as CompositeActionExecutionData;
|
||||||
|
Trace.Info($"Load {compositeAction.Steps?.Count ?? 0} action steps.");
|
||||||
|
Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction?.Steps)}");
|
||||||
|
Trace.Info($"Load: {compositeAction.Outputs?.Count ?? 0} number of outputs");
|
||||||
|
Trace.Info($"Details: {StringUtil.ConvertToJson(compositeAction?.Outputs)}");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(definition.Data.Execution.ExecutionType.ToString());
|
throw new NotSupportedException(definition.Data.Execution.ExecutionType.ToString());
|
||||||
@@ -460,7 +468,7 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNull(setupInfo, nameof(setupInfo));
|
ArgUtil.NotNull(setupInfo, nameof(setupInfo));
|
||||||
ArgUtil.NotNullOrEmpty(setupInfo.Container.Image, nameof(setupInfo.Container.Image));
|
ArgUtil.NotNullOrEmpty(setupInfo.Container.Image, nameof(setupInfo.Container.Image));
|
||||||
|
|
||||||
executionContext.Output($"Pull down action image '{setupInfo.Container.Image}'");
|
executionContext.Output($"##[group]Pull down action image '{setupInfo.Container.Image}'");
|
||||||
|
|
||||||
// Pull down docker image with retry up to 3 times
|
// Pull down docker image with retry up to 3 times
|
||||||
var dockerManger = HostContext.GetService<IDockerCommandManager>();
|
var dockerManger = HostContext.GetService<IDockerCommandManager>();
|
||||||
@@ -484,6 +492,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
executionContext.Output("##[endgroup");
|
||||||
|
|
||||||
if (retryCount == 3 && pullExitCode != 0)
|
if (retryCount == 3 && pullExitCode != 0)
|
||||||
{
|
{
|
||||||
@@ -503,7 +512,7 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNull(setupInfo, nameof(setupInfo));
|
ArgUtil.NotNull(setupInfo, nameof(setupInfo));
|
||||||
ArgUtil.NotNullOrEmpty(setupInfo.Container.Dockerfile, nameof(setupInfo.Container.Dockerfile));
|
ArgUtil.NotNullOrEmpty(setupInfo.Container.Dockerfile, nameof(setupInfo.Container.Dockerfile));
|
||||||
|
|
||||||
executionContext.Output($"Build container for action use: '{setupInfo.Container.Dockerfile}'.");
|
executionContext.Output($"##[group]Build container for action use: '{setupInfo.Container.Dockerfile}'.");
|
||||||
|
|
||||||
// Build docker image with retry up to 3 times
|
// Build docker image with retry up to 3 times
|
||||||
var dockerManger = HostContext.GetService<IDockerCommandManager>();
|
var dockerManger = HostContext.GetService<IDockerCommandManager>();
|
||||||
@@ -533,6 +542,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
|
|
||||||
if (retryCount == 3 && buildExitCode != 0)
|
if (retryCount == 3 && buildExitCode != 0)
|
||||||
{
|
{
|
||||||
@@ -581,7 +591,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
actionDownloadInfos = await jobServer.ResolveActionDownloadInfoAsync(executionContext.Plan.ScopeIdentifier, executionContext.Plan.PlanType, executionContext.Plan.PlanId, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
|
actionDownloadInfos = await jobServer.ResolveActionDownloadInfoAsync(executionContext.Global.Plan.ScopeIdentifier, executionContext.Global.Plan.PlanType, executionContext.Global.Plan.PlanId, new WebApi.ActionReferenceList { Actions = actionReferences }, executionContext.CancellationToken);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (attempt < 3)
|
catch (Exception ex) when (attempt < 3)
|
||||||
@@ -939,7 +949,7 @@ namespace GitHub.Runner.Worker
|
|||||||
if (string.IsNullOrEmpty(authToken))
|
if (string.IsNullOrEmpty(authToken))
|
||||||
{
|
{
|
||||||
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
||||||
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
authToken = executionContext.Global.Variables.Get("PREVIEW_ACTION_TOKEN");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(authToken))
|
if (!string.IsNullOrEmpty(authToken))
|
||||||
@@ -1038,6 +1048,11 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||||
|
{
|
||||||
|
Trace.Info($"Action composite: {(actionDefinitionData.Execution as CompositeActionExecutionData).Steps}, no more preparation.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString());
|
throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString());
|
||||||
@@ -1148,6 +1163,7 @@ namespace GitHub.Runner.Worker
|
|||||||
NodeJS,
|
NodeJS,
|
||||||
Plugin,
|
Plugin,
|
||||||
Script,
|
Script,
|
||||||
|
Composite,
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ContainerActionExecutionData : ActionExecutionData
|
public sealed class ContainerActionExecutionData : ActionExecutionData
|
||||||
@@ -1204,6 +1220,15 @@ namespace GitHub.Runner.Worker
|
|||||||
public override bool HasPost => false;
|
public override bool HasPost => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class CompositeActionExecutionData : ActionExecutionData
|
||||||
|
{
|
||||||
|
public override ActionExecutionType ExecutionType => ActionExecutionType.Composite;
|
||||||
|
public override bool HasPre => false;
|
||||||
|
public override bool HasPost => false;
|
||||||
|
public List<Pipelines.ActionStep> Steps { get; set; }
|
||||||
|
public MappingToken Outputs { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class ActionExecutionData
|
public abstract class ActionExecutionData
|
||||||
{
|
{
|
||||||
private string _initCondition = $"{Constants.Expressions.Always}()";
|
private string _initCondition = $"{Constants.Expressions.Always}()";
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using YamlDotNet.Core;
|
|||||||
using YamlDotNet.Core.Events;
|
using YamlDotNet.Core.Events;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
{
|
{
|
||||||
@@ -22,6 +23,8 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile);
|
ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile);
|
||||||
|
|
||||||
|
DictionaryContextData EvaluateCompositeOutputs(IExecutionContext executionContext, TemplateToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||||
|
|
||||||
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
List<string> EvaluateContainerArguments(IExecutionContext executionContext, SequenceToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||||
|
|
||||||
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
Dictionary<string, string> EvaluateContainerEnvironment(IExecutionContext executionContext, MappingToken token, IDictionary<string, PipelineContextData> extraExpressionValues);
|
||||||
@@ -32,8 +35,6 @@ namespace GitHub.Runner.Worker
|
|||||||
public sealed class ActionManifestManager : RunnerService, IActionManifestManager
|
public sealed class ActionManifestManager : RunnerService, IActionManifestManager
|
||||||
{
|
{
|
||||||
private TemplateSchema _actionManifestSchema;
|
private TemplateSchema _actionManifestSchema;
|
||||||
private IReadOnlyList<String> _fileTable;
|
|
||||||
|
|
||||||
public override void Initialize(IHostContext hostContext)
|
public override void Initialize(IHostContext hostContext)
|
||||||
{
|
{
|
||||||
base.Initialize(hostContext);
|
base.Initialize(hostContext);
|
||||||
@@ -54,25 +55,45 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile)
|
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile)
|
||||||
{
|
{
|
||||||
var context = CreateContext(executionContext);
|
var templateContext = CreateTemplateContext(executionContext);
|
||||||
ActionDefinitionData actionDefinition = new ActionDefinitionData();
|
ActionDefinitionData actionDefinition = new ActionDefinitionData();
|
||||||
|
|
||||||
|
// Clean up file name real quick
|
||||||
|
// Instead of using Regex which can be computationally expensive,
|
||||||
|
// we can just remove the # of characters from the fileName according to the length of the basePath
|
||||||
|
string basePath = HostContext.GetDirectory(WellKnownDirectory.Actions);
|
||||||
|
string fileRelativePath = manifestFile;
|
||||||
|
if (manifestFile.Contains(basePath))
|
||||||
|
{
|
||||||
|
fileRelativePath = manifestFile.Remove(0, basePath.Length + 1);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var token = default(TemplateToken);
|
var token = default(TemplateToken);
|
||||||
|
|
||||||
// Get the file ID
|
// Get the file ID
|
||||||
var fileId = context.GetFileId(manifestFile);
|
var fileId = templateContext.GetFileId(fileRelativePath);
|
||||||
_fileTable = context.GetFileTable();
|
|
||||||
|
// Add this file to the FileTable in executionContext if it hasn't been added already
|
||||||
|
// we use > since fileID is 1 indexed
|
||||||
|
if (fileId > executionContext.Global.FileTable.Count)
|
||||||
|
{
|
||||||
|
executionContext.Global.FileTable.Add(fileRelativePath);
|
||||||
|
}
|
||||||
|
|
||||||
// Read the file
|
// Read the file
|
||||||
var fileContent = File.ReadAllText(manifestFile);
|
var fileContent = File.ReadAllText(manifestFile);
|
||||||
using (var stringReader = new StringReader(fileContent))
|
using (var stringReader = new StringReader(fileContent))
|
||||||
{
|
{
|
||||||
var yamlObjectReader = new YamlObjectReader(null, stringReader);
|
var yamlObjectReader = new YamlObjectReader(fileId, stringReader);
|
||||||
token = TemplateReader.Read(context, "action-root", yamlObjectReader, fileId, out _);
|
token = TemplateReader.Read(templateContext, "action-root", yamlObjectReader, fileId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionMapping = token.AssertMapping("action manifest root");
|
var actionMapping = token.AssertMapping("action manifest root");
|
||||||
|
var actionOutputs = default(MappingToken);
|
||||||
|
var actionRunValueToken = default(TemplateToken);
|
||||||
|
|
||||||
foreach (var actionPair in actionMapping)
|
foreach (var actionPair in actionMapping)
|
||||||
{
|
{
|
||||||
var propertyName = actionPair.Key.AssertString($"action.yml property key");
|
var propertyName = actionPair.Key.AssertString($"action.yml property key");
|
||||||
@@ -83,44 +104,61 @@ namespace GitHub.Runner.Worker
|
|||||||
actionDefinition.Name = actionPair.Value.AssertString("name").Value;
|
actionDefinition.Name = actionPair.Value.AssertString("name").Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "outputs":
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||||
|
{
|
||||||
|
actionOutputs = actionPair.Value.AssertMapping("outputs");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Trace.Info($"Ignore action property outputs. Outputs for a whole action is not supported yet.");
|
||||||
|
break;
|
||||||
|
|
||||||
case "description":
|
case "description":
|
||||||
actionDefinition.Description = actionPair.Value.AssertString("description").Value;
|
actionDefinition.Description = actionPair.Value.AssertString("description").Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "inputs":
|
case "inputs":
|
||||||
ConvertInputs(context, actionPair.Value, actionDefinition);
|
ConvertInputs(actionPair.Value, actionDefinition);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "runs":
|
case "runs":
|
||||||
actionDefinition.Execution = ConvertRuns(context, actionPair.Value);
|
// Defer runs token evaluation to after for loop to ensure that order of outputs doesn't matter.
|
||||||
|
actionRunValueToken = actionPair.Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Trace.Info($"Ignore action property {propertyName}.");
|
Trace.Info($"Ignore action property {propertyName}.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluate Runs Last
|
||||||
|
if (actionRunValueToken != null)
|
||||||
|
{
|
||||||
|
actionDefinition.Execution = ConvertRuns(executionContext, templateContext, actionRunValueToken, fileRelativePath, actionOutputs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
context.Errors.Add(ex);
|
templateContext.Errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Errors.Count > 0)
|
if (templateContext.Errors.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var error in context.Errors)
|
foreach (var error in templateContext.Errors)
|
||||||
{
|
{
|
||||||
Trace.Error($"Action.yml load error: {error.Message}");
|
Trace.Error($"Action.yml load error: {error.Message}");
|
||||||
executionContext.Error(error.Message);
|
executionContext.Error(error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"Fail to load {manifestFile}");
|
throw new ArgumentException($"Fail to load {fileRelativePath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionDefinition.Execution == null)
|
if (actionDefinition.Execution == null)
|
||||||
{
|
{
|
||||||
executionContext.Debug($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}");
|
executionContext.Debug($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}");
|
||||||
throw new ArgumentException($"Top level 'runs:' section is required for {manifestFile}");
|
throw new ArgumentException($"Top level 'runs:' section is required for {fileRelativePath}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -130,6 +168,33 @@ namespace GitHub.Runner.Worker
|
|||||||
return actionDefinition;
|
return actionDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DictionaryContextData EvaluateCompositeOutputs(
|
||||||
|
IExecutionContext executionContext,
|
||||||
|
TemplateToken token,
|
||||||
|
IDictionary<string, PipelineContextData> extraExpressionValues)
|
||||||
|
{
|
||||||
|
var result = default(DictionaryContextData);
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
var templateContext = CreateTemplateContext(executionContext, extraExpressionValues);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
token = TemplateEvaluator.Evaluate(templateContext, "outputs", token, 0, null, omitHeader: true);
|
||||||
|
templateContext.Errors.Check();
|
||||||
|
result = token.ToContextData().AssertDictionary("composite outputs");
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||||
|
{
|
||||||
|
templateContext.Errors.Add(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
templateContext.Errors.Check();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result ?? new DictionaryContextData();
|
||||||
|
}
|
||||||
|
|
||||||
public List<string> EvaluateContainerArguments(
|
public List<string> EvaluateContainerArguments(
|
||||||
IExecutionContext executionContext,
|
IExecutionContext executionContext,
|
||||||
SequenceToken token,
|
SequenceToken token,
|
||||||
@@ -139,11 +204,11 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
if (token != null)
|
if (token != null)
|
||||||
{
|
{
|
||||||
var context = CreateContext(executionContext, extraExpressionValues);
|
var templateContext = CreateTemplateContext(executionContext, extraExpressionValues);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "container-runs-args", token, 0, null, omitHeader: true);
|
var evaluateResult = TemplateEvaluator.Evaluate(templateContext, "container-runs-args", token, 0, null, omitHeader: true);
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
|
|
||||||
Trace.Info($"Arguments evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
Trace.Info($"Arguments evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
||||||
|
|
||||||
@@ -160,10 +225,10 @@ namespace GitHub.Runner.Worker
|
|||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||||
{
|
{
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
context.Errors.Add(ex);
|
templateContext.Errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -178,11 +243,11 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
if (token != null)
|
if (token != null)
|
||||||
{
|
{
|
||||||
var context = CreateContext(executionContext, extraExpressionValues);
|
var templateContext = CreateTemplateContext(executionContext, extraExpressionValues);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "container-runs-env", token, 0, null, omitHeader: true);
|
var evaluateResult = TemplateEvaluator.Evaluate(templateContext, "container-runs-env", token, 0, null, omitHeader: true);
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
|
|
||||||
Trace.Info($"Environments evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
Trace.Info($"Environments evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
||||||
|
|
||||||
@@ -204,10 +269,10 @@ namespace GitHub.Runner.Worker
|
|||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||||
{
|
{
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
context.Errors.Add(ex);
|
templateContext.Errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -221,11 +286,11 @@ namespace GitHub.Runner.Worker
|
|||||||
string result = "";
|
string result = "";
|
||||||
if (token != null)
|
if (token != null)
|
||||||
{
|
{
|
||||||
var context = CreateContext(executionContext);
|
var templateContext = CreateTemplateContext(executionContext);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var evaluateResult = TemplateEvaluator.Evaluate(context, "input-default-context", token, 0, null, omitHeader: true);
|
var evaluateResult = TemplateEvaluator.Evaluate(templateContext, "input-default-context", token, 0, null, omitHeader: true);
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
|
|
||||||
Trace.Info($"Input '{inputName}': default value evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
Trace.Info($"Input '{inputName}': default value evaluate result: {StringUtil.ConvertToJson(evaluateResult)}");
|
||||||
|
|
||||||
@@ -235,16 +300,16 @@ namespace GitHub.Runner.Worker
|
|||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
catch (Exception ex) when (!(ex is TemplateValidationException))
|
||||||
{
|
{
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
context.Errors.Add(ex);
|
templateContext.Errors.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Errors.Check();
|
templateContext.Errors.Check();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TemplateContext CreateContext(
|
private TemplateContext CreateTemplateContext(
|
||||||
IExecutionContext executionContext,
|
IExecutionContext executionContext,
|
||||||
IDictionary<string, PipelineContextData> extraExpressionValues = null)
|
IDictionary<string, PipelineContextData> extraExpressionValues = null)
|
||||||
{
|
{
|
||||||
@@ -281,21 +346,21 @@ namespace GitHub.Runner.Worker
|
|||||||
result.ExpressionFunctions.Add(item);
|
result.ExpressionFunctions.Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file table
|
// Add the file table from the Execution Context
|
||||||
if (_fileTable?.Count > 0)
|
for (var i = 0; i < executionContext.Global.FileTable.Count; i++)
|
||||||
{
|
{
|
||||||
for (var i = 0 ; i < _fileTable.Count ; i++)
|
result.GetFileId(executionContext.Global.FileTable[i]);
|
||||||
{
|
|
||||||
result.GetFileId(_fileTable[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionExecutionData ConvertRuns(
|
private ActionExecutionData ConvertRuns(
|
||||||
TemplateContext context,
|
IExecutionContext executionContext,
|
||||||
TemplateToken inputsToken)
|
TemplateContext templateContext,
|
||||||
|
TemplateToken inputsToken,
|
||||||
|
String fileRelativePath,
|
||||||
|
MappingToken outputs = null)
|
||||||
{
|
{
|
||||||
var runsMapping = inputsToken.AssertMapping("runs");
|
var runsMapping = inputsToken.AssertMapping("runs");
|
||||||
var usingToken = default(StringToken);
|
var usingToken = default(StringToken);
|
||||||
@@ -311,6 +376,8 @@ namespace GitHub.Runner.Worker
|
|||||||
var postToken = default(StringToken);
|
var postToken = default(StringToken);
|
||||||
var postEntrypointToken = default(StringToken);
|
var postEntrypointToken = default(StringToken);
|
||||||
var postIfToken = default(StringToken);
|
var postIfToken = default(StringToken);
|
||||||
|
var steps = default(List<Pipelines.Step>);
|
||||||
|
|
||||||
foreach (var run in runsMapping)
|
foreach (var run in runsMapping)
|
||||||
{
|
{
|
||||||
var runsKey = run.Key.AssertString("runs key").Value;
|
var runsKey = run.Key.AssertString("runs key").Value;
|
||||||
@@ -355,6 +422,15 @@ namespace GitHub.Runner.Worker
|
|||||||
case "pre-if":
|
case "pre-if":
|
||||||
preIfToken = run.Value.AssertString("pre-if");
|
preIfToken = run.Value.AssertString("pre-if");
|
||||||
break;
|
break;
|
||||||
|
case "steps":
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||||
|
{
|
||||||
|
var stepsToken = run.Value.AssertSequence("steps");
|
||||||
|
steps = PipelineTemplateConverter.ConvertToSteps(templateContext, stepsToken);
|
||||||
|
templateContext.Errors.Check();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new Exception("You aren't supposed to be using Composite Actions yet!");
|
||||||
default:
|
default:
|
||||||
Trace.Info($"Ignore run property {runsKey}.");
|
Trace.Info($"Ignore run property {runsKey}.");
|
||||||
break;
|
break;
|
||||||
@@ -367,7 +443,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(imageToken?.Value))
|
if (string.IsNullOrEmpty(imageToken?.Value))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException($"Image is not provided.");
|
throw new ArgumentNullException($"You are using a Container Action but an image is not provided in {fileRelativePath}.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -388,7 +464,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(mainToken?.Value))
|
if (string.IsNullOrEmpty(mainToken?.Value))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException($"Entry javascript file is not provided.");
|
throw new ArgumentNullException($"You are using a JavaScript Action but there is not an entry JavaScript file provided in {fileRelativePath}.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -402,6 +478,21 @@ namespace GitHub.Runner.Worker
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||||
|
{
|
||||||
|
if (steps == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException($"You are using a composite action but there are no steps provided in {fileRelativePath}.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new CompositeActionExecutionData()
|
||||||
|
{
|
||||||
|
Steps = steps.Cast<Pipelines.ActionStep>().ToList(),
|
||||||
|
Outputs = outputs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
|
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
|
||||||
@@ -419,7 +510,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ConvertInputs(
|
private void ConvertInputs(
|
||||||
TemplateContext context,
|
|
||||||
TemplateToken inputsToken,
|
TemplateToken inputsToken,
|
||||||
ActionDefinitionData actionDefinition)
|
ActionDefinitionData actionDefinition)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -136,12 +136,12 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup container stephost for running inside the container.
|
// Setup container stephost for running inside the container.
|
||||||
if (ExecutionContext.Container != null)
|
if (ExecutionContext.Global.Container != null)
|
||||||
{
|
{
|
||||||
// Make sure required container is already created.
|
// Make sure required container is already created.
|
||||||
ArgUtil.NotNullOrEmpty(ExecutionContext.Container.ContainerId, nameof(ExecutionContext.Container.ContainerId));
|
ArgUtil.NotNullOrEmpty(ExecutionContext.Global.Container.ContainerId, nameof(ExecutionContext.Global.Container.ContainerId));
|
||||||
var containerStepHost = HostContext.CreateService<IContainerStepHost>();
|
var containerStepHost = HostContext.CreateService<IContainerStepHost>();
|
||||||
containerStepHost.Container = ExecutionContext.Container;
|
containerStepHost.Container = ExecutionContext.Global.Container;
|
||||||
stepHost = containerStepHost;
|
stepHost = containerStepHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ namespace GitHub.Runner.Worker
|
|||||||
handlerData,
|
handlerData,
|
||||||
inputs,
|
inputs,
|
||||||
environment,
|
environment,
|
||||||
ExecutionContext.Variables,
|
ExecutionContext.Global.Variables,
|
||||||
actionDirectory: definition.Directory);
|
actionDirectory: definition.Directory);
|
||||||
|
|
||||||
// Print out action details
|
// Print out action details
|
||||||
|
|||||||
@@ -91,7 +91,10 @@ namespace GitHub.Runner.Worker
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check docker client/server version
|
// Check docker client/server version
|
||||||
|
executionContext.Output("##[group]Checking docker version");
|
||||||
DockerVersion dockerVersion = await _dockerManger.DockerVersion(executionContext);
|
DockerVersion dockerVersion = await _dockerManger.DockerVersion(executionContext);
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
|
|
||||||
ArgUtil.NotNull(dockerVersion.ServerVersion, nameof(dockerVersion.ServerVersion));
|
ArgUtil.NotNull(dockerVersion.ServerVersion, nameof(dockerVersion.ServerVersion));
|
||||||
ArgUtil.NotNull(dockerVersion.ClientVersion, nameof(dockerVersion.ClientVersion));
|
ArgUtil.NotNull(dockerVersion.ClientVersion, nameof(dockerVersion.ClientVersion));
|
||||||
|
|
||||||
@@ -111,7 +114,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clean up containers left by previous runs
|
// Clean up containers left by previous runs
|
||||||
executionContext.Debug($"Delete stale containers from previous jobs");
|
executionContext.Output("##[group]Clean up resources from previous jobs");
|
||||||
var staleContainers = await _dockerManger.DockerPS(executionContext, $"--all --quiet --no-trunc --filter \"label={_dockerManger.DockerInstanceLabel}\"");
|
var staleContainers = await _dockerManger.DockerPS(executionContext, $"--all --quiet --no-trunc --filter \"label={_dockerManger.DockerInstanceLabel}\"");
|
||||||
foreach (var staleContainer in staleContainers)
|
foreach (var staleContainer in staleContainers)
|
||||||
{
|
{
|
||||||
@@ -122,18 +125,25 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executionContext.Debug($"Delete stale container networks from previous jobs");
|
|
||||||
int networkPruneExitCode = await _dockerManger.DockerNetworkPrune(executionContext);
|
int networkPruneExitCode = await _dockerManger.DockerNetworkPrune(executionContext);
|
||||||
if (networkPruneExitCode != 0)
|
if (networkPruneExitCode != 0)
|
||||||
{
|
{
|
||||||
executionContext.Warning($"Delete stale container networks failed, docker network prune fail with exit code {networkPruneExitCode}");
|
executionContext.Warning($"Delete stale container networks failed, docker network prune fail with exit code {networkPruneExitCode}");
|
||||||
}
|
}
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
|
|
||||||
// Create local docker network for this job to avoid port conflict when multiple runners run on same machine.
|
// Create local docker network for this job to avoid port conflict when multiple runners run on same machine.
|
||||||
// All containers within a job join the same network
|
// All containers within a job join the same network
|
||||||
|
executionContext.Output("##[group]Create local container network");
|
||||||
var containerNetwork = $"github_network_{Guid.NewGuid().ToString("N")}";
|
var containerNetwork = $"github_network_{Guid.NewGuid().ToString("N")}";
|
||||||
await CreateContainerNetworkAsync(executionContext, containerNetwork);
|
await CreateContainerNetworkAsync(executionContext, containerNetwork);
|
||||||
executionContext.JobContext.Container["network"] = new StringContextData(containerNetwork);
|
executionContext.JobContext.Container["network"] = new StringContextData(containerNetwork);
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
|
|
||||||
|
if (Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||||
|
{
|
||||||
|
IOUtil.CopyDirectory(HostContext.GetDirectory(WellKnownDirectory.Externals), Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var container in containers)
|
foreach (var container in containers)
|
||||||
{
|
{
|
||||||
@@ -141,10 +151,12 @@ namespace GitHub.Runner.Worker
|
|||||||
await StartContainerAsync(executionContext, container);
|
await StartContainerAsync(executionContext, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executionContext.Output("##[group]Waiting for all services to be ready");
|
||||||
foreach (var container in containers.Where(c => !c.IsJobContainer))
|
foreach (var container in containers.Where(c => !c.IsJobContainer))
|
||||||
{
|
{
|
||||||
await ContainerHealthcheck(executionContext, container);
|
await ContainerHealthcheck(executionContext, container);
|
||||||
}
|
}
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopContainersAsync(IExecutionContext executionContext, object data)
|
public async Task StopContainersAsync(IExecutionContext executionContext, object data)
|
||||||
@@ -173,6 +185,10 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info($"Container name: {container.ContainerName}");
|
Trace.Info($"Container name: {container.ContainerName}");
|
||||||
Trace.Info($"Container image: {container.ContainerImage}");
|
Trace.Info($"Container image: {container.ContainerImage}");
|
||||||
Trace.Info($"Container options: {container.ContainerCreateOptions}");
|
Trace.Info($"Container options: {container.ContainerCreateOptions}");
|
||||||
|
|
||||||
|
var groupName = container.IsJobContainer ? "Starting job container" : $"Starting {container.ContainerNetworkAlias} service container";
|
||||||
|
executionContext.Output($"##[group]{groupName}");
|
||||||
|
|
||||||
foreach (var port in container.UserPortMappings)
|
foreach (var port in container.UserPortMappings)
|
||||||
{
|
{
|
||||||
Trace.Info($"User provided port: {port.Value}");
|
Trace.Info($"User provided port: {port.Value}");
|
||||||
@@ -225,7 +241,14 @@ namespace GitHub.Runner.Worker
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals))));
|
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals))));
|
||||||
#else
|
#else
|
||||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
if (Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||||
|
{
|
||||||
|
container.MountVolumes.Add(new MountVolume(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Externals), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Externals)), true));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp))));
|
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Temp), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Temp))));
|
||||||
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions))));
|
container.MountVolumes.Add(new MountVolume(HostContext.GetDirectory(WellKnownDirectory.Actions), container.TranslateToContainerPath(HostContext.GetDirectory(WellKnownDirectory.Actions))));
|
||||||
@@ -304,6 +327,7 @@ namespace GitHub.Runner.Worker
|
|||||||
container.ContainerRuntimePath = DockerUtil.ParsePathFromConfigEnv(containerEnv);
|
container.ContainerRuntimePath = DockerUtil.ParsePathFromConfigEnv(containerEnv);
|
||||||
executionContext.JobContext.Container["id"] = new StringContextData(container.ContainerId);
|
executionContext.JobContext.Container["id"] = new StringContextData(container.ContainerId);
|
||||||
}
|
}
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopContainerAsync(IExecutionContext executionContext, ContainerInfo container)
|
private async Task StopContainerAsync(IExecutionContext executionContext, ContainerInfo container)
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
executionContext.Debug("Zipping diagnostic files.");
|
executionContext.Debug("Zipping diagnostic files.");
|
||||||
|
|
||||||
string buildNumber = executionContext.Variables.Build_Number ?? "UnknownBuildNumber";
|
string buildNumber = executionContext.Global.Variables.Build_Number ?? "UnknownBuildNumber";
|
||||||
string buildName = $"Build {buildNumber}";
|
string buildName = $"Build {buildNumber}";
|
||||||
string phaseName = executionContext.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";
|
string phaseName = executionContext.Global.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";
|
||||||
|
|
||||||
// zip the files
|
// zip the files
|
||||||
string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
|
string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
@@ -43,40 +44,30 @@ namespace GitHub.Runner.Worker
|
|||||||
string ResultCode { get; set; }
|
string ResultCode { get; set; }
|
||||||
TaskResult? CommandResult { get; set; }
|
TaskResult? CommandResult { get; set; }
|
||||||
CancellationToken CancellationToken { get; }
|
CancellationToken CancellationToken { get; }
|
||||||
List<ServiceEndpoint> Endpoints { get; }
|
GlobalContext Global { get; }
|
||||||
TaskOrchestrationPlanReference Plan { get; }
|
|
||||||
|
|
||||||
PlanFeatures Features { get; }
|
|
||||||
Variables Variables { get; }
|
|
||||||
Dictionary<string, string> IntraActionState { get; }
|
Dictionary<string, string> IntraActionState { get; }
|
||||||
IDictionary<String, IDictionary<String, String>> JobDefaults { get; }
|
|
||||||
Dictionary<string, VariableValue> JobOutputs { get; }
|
Dictionary<string, VariableValue> JobOutputs { get; }
|
||||||
IDictionary<String, String> EnvironmentVariables { get; }
|
|
||||||
IDictionary<String, ContextScope> Scopes { get; }
|
|
||||||
IList<String> FileTable { get; }
|
|
||||||
StepsContext StepsContext { get; }
|
|
||||||
DictionaryContextData ExpressionValues { get; }
|
DictionaryContextData ExpressionValues { get; }
|
||||||
IList<IFunctionInfo> ExpressionFunctions { get; }
|
IList<IFunctionInfo> ExpressionFunctions { get; }
|
||||||
List<string> PrependPath { get; }
|
|
||||||
ContainerInfo Container { get; set; }
|
|
||||||
List<ContainerInfo> ServiceContainers { get; }
|
|
||||||
JobContext JobContext { get; }
|
JobContext JobContext { get; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has JobSteps
|
// Only job level ExecutionContext has JobSteps
|
||||||
Queue<IStep> JobSteps { get; }
|
List<IStep> JobSteps { get; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has PostJobSteps
|
// Only job level ExecutionContext has PostJobSteps
|
||||||
Stack<IStep> PostJobSteps { get; }
|
Stack<IStep> PostJobSteps { get; }
|
||||||
|
|
||||||
bool EchoOnActionCommand { get; set; }
|
bool EchoOnActionCommand { get; set; }
|
||||||
|
|
||||||
|
ExecutionContext Root { get; }
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
||||||
void CancelToken();
|
void CancelToken();
|
||||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null);
|
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, CancellationTokenSource cancellationTokenSource = null);
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
bool WriteDebug { get; }
|
|
||||||
long Write(string tag, string message);
|
long Write(string tag, string message);
|
||||||
void QueueAttachFile(string type, string name, string filePath);
|
void QueueAttachFile(string type, string name, string filePath);
|
||||||
|
|
||||||
@@ -105,6 +96,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// others
|
// others
|
||||||
void ForceTaskComplete();
|
void ForceTaskComplete();
|
||||||
void RegisterPostJobStep(IStep step);
|
void RegisterPostJobStep(IStep step);
|
||||||
|
IStep CreateCompositeStep(string scopeName, IActionRunner step, DictionaryContextData inputsData, Dictionary<string, string> envData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||||
@@ -141,25 +133,16 @@ namespace GitHub.Runner.Worker
|
|||||||
public string ContextName { get; private set; }
|
public string ContextName { get; private set; }
|
||||||
public Task ForceCompleted => _forceCompleted.Task;
|
public Task ForceCompleted => _forceCompleted.Task;
|
||||||
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
public CancellationToken CancellationToken => _cancellationTokenSource.Token;
|
||||||
public List<ServiceEndpoint> Endpoints { get; private set; }
|
|
||||||
public TaskOrchestrationPlanReference Plan { get; private set; }
|
|
||||||
public Variables Variables { get; private set; }
|
|
||||||
public Dictionary<string, string> IntraActionState { get; private set; }
|
public Dictionary<string, string> IntraActionState { get; private set; }
|
||||||
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; private set; }
|
|
||||||
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
||||||
public IDictionary<String, String> EnvironmentVariables { get; private set; }
|
|
||||||
public IDictionary<String, ContextScope> Scopes { get; private set; }
|
|
||||||
public IList<String> FileTable { get; private set; }
|
|
||||||
public StepsContext StepsContext { get; private set; }
|
|
||||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||||
public bool WriteDebug { get; private set; }
|
|
||||||
public List<string> PrependPath { get; private set; }
|
// Shared pointer across job-level execution context and step-level execution contexts
|
||||||
public ContainerInfo Container { get; set; }
|
public GlobalContext Global { get; private set; }
|
||||||
public List<ContainerInfo> ServiceContainers { get; private set; }
|
|
||||||
|
|
||||||
// Only job level ExecutionContext has JobSteps
|
// Only job level ExecutionContext has JobSteps
|
||||||
public Queue<IStep> JobSteps { get; private set; }
|
public List<IStep> JobSteps { get; private set; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has PostJobSteps
|
// Only job level ExecutionContext has PostJobSteps
|
||||||
public Stack<IStep> PostJobSteps { get; private set; }
|
public Stack<IStep> PostJobSteps { get; private set; }
|
||||||
@@ -169,7 +152,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public bool EchoOnActionCommand { get; set; }
|
public bool EchoOnActionCommand { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public TaskResult? Result
|
public TaskResult? Result
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -200,9 +182,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlanFeatures Features { get; private set; }
|
public ExecutionContext Root
|
||||||
|
|
||||||
private ExecutionContext Root
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -266,18 +246,44 @@ namespace GitHub.Runner.Worker
|
|||||||
Root.PostJobSteps.Push(step);
|
Root.PostJobSteps.Push(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null)
|
/// <summary>
|
||||||
|
/// Helper function used in CompositeActionHandler::RunAsync to
|
||||||
|
/// add a child node, aka a step, to the current job to the Root.JobSteps based on the location.
|
||||||
|
/// </summary>
|
||||||
|
public IStep CreateCompositeStep(
|
||||||
|
string scopeName,
|
||||||
|
IActionRunner step,
|
||||||
|
DictionaryContextData inputsData,
|
||||||
|
Dictionary<string, string> envData)
|
||||||
|
{
|
||||||
|
step.ExecutionContext = Root.CreateChild(_record.Id, step.DisplayName, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||||
|
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||||
|
step.ExecutionContext.ExpressionValues["steps"] = Global.StepsContext.GetScope(step.ExecutionContext.GetFullyQualifiedContextName());
|
||||||
|
|
||||||
|
// Add the composite action environment variables to each step.
|
||||||
|
#if OS_WINDOWS
|
||||||
|
var envContext = new DictionaryContextData();
|
||||||
|
#else
|
||||||
|
var envContext = new CaseSensitiveDictionaryContextData();
|
||||||
|
#endif
|
||||||
|
foreach (var pair in envData)
|
||||||
|
{
|
||||||
|
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||||
|
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, CancellationTokenSource cancellationTokenSource = null)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
var child = new ExecutionContext();
|
var child = new ExecutionContext();
|
||||||
child.Initialize(HostContext);
|
child.Initialize(HostContext);
|
||||||
|
child.Global = Global;
|
||||||
child.ScopeName = scopeName;
|
child.ScopeName = scopeName;
|
||||||
child.ContextName = contextName;
|
child.ContextName = contextName;
|
||||||
child.Features = Features;
|
|
||||||
child.Variables = Variables;
|
|
||||||
child.Endpoints = Endpoints;
|
|
||||||
child.Plan = Plan;
|
|
||||||
if (intraActionState == null)
|
if (intraActionState == null)
|
||||||
{
|
{
|
||||||
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -286,11 +292,6 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
child.IntraActionState = intraActionState;
|
child.IntraActionState = intraActionState;
|
||||||
}
|
}
|
||||||
child.EnvironmentVariables = EnvironmentVariables;
|
|
||||||
child.JobDefaults = JobDefaults;
|
|
||||||
child.Scopes = Scopes;
|
|
||||||
child.FileTable = FileTable;
|
|
||||||
child.StepsContext = StepsContext;
|
|
||||||
foreach (var pair in ExpressionValues)
|
foreach (var pair in ExpressionValues)
|
||||||
{
|
{
|
||||||
child.ExpressionValues[pair.Key] = pair.Value;
|
child.ExpressionValues[pair.Key] = pair.Value;
|
||||||
@@ -299,12 +300,8 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
child.ExpressionFunctions.Add(item);
|
child.ExpressionFunctions.Add(item);
|
||||||
}
|
}
|
||||||
child._cancellationTokenSource = new CancellationTokenSource();
|
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||||
child.WriteDebug = WriteDebug;
|
|
||||||
child._parentExecutionContext = this;
|
child._parentExecutionContext = this;
|
||||||
child.PrependPath = PrependPath;
|
|
||||||
child.Container = Container;
|
|
||||||
child.ServiceContainers = ServiceContainers;
|
|
||||||
child.EchoOnActionCommand = EchoOnActionCommand;
|
child.EchoOnActionCommand = EchoOnActionCommand;
|
||||||
|
|
||||||
if (recordOrder != null)
|
if (recordOrder != null)
|
||||||
@@ -315,9 +312,15 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
child.InitializeTimelineRecord(_mainTimelineId, recordId, _record.Id, ExecutionContextType.Task, displayName, refName, ++_childTimelineRecordOrder);
|
child.InitializeTimelineRecord(_mainTimelineId, recordId, _record.Id, ExecutionContextType.Task, displayName, refName, ++_childTimelineRecordOrder);
|
||||||
}
|
}
|
||||||
|
if (logger != null)
|
||||||
child._logger = HostContext.CreateService<IPagingLogger>();
|
{
|
||||||
child._logger.Setup(_mainTimelineId, recordId);
|
child._logger = logger;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child._logger = HostContext.CreateService<IPagingLogger>();
|
||||||
|
child._logger.Setup(_mainTimelineId, recordId);
|
||||||
|
}
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@@ -375,10 +378,11 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
_logger.End();
|
_logger.End();
|
||||||
|
|
||||||
|
// todo: Skip if generated context name. After M271-ish the server will never send an empty context name. Generated context names will start with "__"
|
||||||
if (!string.IsNullOrEmpty(ContextName))
|
if (!string.IsNullOrEmpty(ContextName))
|
||||||
{
|
{
|
||||||
StepsContext.SetOutcome(ScopeName, ContextName, (Outcome ?? Result ?? TaskResult.Succeeded).ToActionResult());
|
Global.StepsContext.SetOutcome(ScopeName, ContextName, (Outcome ?? Result ?? TaskResult.Succeeded).ToActionResult());
|
||||||
StepsContext.SetConclusion(ScopeName, ContextName, (Result ?? TaskResult.Succeeded).ToActionResult());
|
Global.StepsContext.SetConclusion(ScopeName, ContextName, (Result ?? TaskResult.Succeeded).ToActionResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Value;
|
return Result.Value;
|
||||||
@@ -437,7 +441,8 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
ArgUtil.NotNullOrEmpty(name, nameof(name));
|
ArgUtil.NotNullOrEmpty(name, nameof(name));
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(ContextName))
|
// todo: Skip if generated context name. After M271-ish the server will never send an empty context name. Generated context names will start with "__"
|
||||||
|
if (string.IsNullOrEmpty(ContextName))
|
||||||
{
|
{
|
||||||
reference = null;
|
reference = null;
|
||||||
return;
|
return;
|
||||||
@@ -445,7 +450,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
// todo: restrict multiline?
|
// todo: restrict multiline?
|
||||||
|
|
||||||
StepsContext.SetOutput(ScopeName, ContextName, name, value, out reference);
|
Global.StepsContext.SetOutput(ScopeName, ContextName, name, value, out reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTimeout(TimeSpan? timeout)
|
public void SetTimeout(TimeSpan? timeout)
|
||||||
@@ -579,43 +584,35 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
|
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
|
||||||
|
|
||||||
|
Global = new GlobalContext();
|
||||||
|
|
||||||
// Plan
|
// Plan
|
||||||
Plan = message.Plan;
|
Global.Plan = message.Plan;
|
||||||
Features = PlanUtil.GetFeatures(message.Plan);
|
Global.Features = PlanUtil.GetFeatures(message.Plan);
|
||||||
|
|
||||||
// Endpoints
|
// Endpoints
|
||||||
Endpoints = message.Resources.Endpoints;
|
Global.Endpoints = message.Resources.Endpoints;
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
Variables = new Variables(HostContext, message.Variables);
|
Global.Variables = new Variables(HostContext, message.Variables);
|
||||||
|
|
||||||
// Environment variables shared across all actions
|
// Environment variables shared across all actions
|
||||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer);
|
Global.EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer);
|
||||||
|
|
||||||
// Job defaults shared across all actions
|
// Job defaults shared across all actions
|
||||||
JobDefaults = new Dictionary<string, IDictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
Global.JobDefaults = new Dictionary<string, IDictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Job Outputs
|
// Job Outputs
|
||||||
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Service container info
|
// Service container info
|
||||||
ServiceContainers = new List<ContainerInfo>();
|
Global.ServiceContainers = new List<ContainerInfo>();
|
||||||
|
|
||||||
// Steps context (StepsRunner manages adding the scoped steps context)
|
// Steps context (StepsRunner manages adding the scoped steps context)
|
||||||
StepsContext = new StepsContext();
|
Global.StepsContext = new StepsContext();
|
||||||
|
|
||||||
// Scopes
|
|
||||||
Scopes = new Dictionary<String, ContextScope>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
if (message.Scopes?.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var scope in message.Scopes)
|
|
||||||
{
|
|
||||||
Scopes[scope.Name] = scope;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// File table
|
// File table
|
||||||
FileTable = new List<String>(message.FileTable ?? new string[0]);
|
Global.FileTable = new List<String>(message.FileTable ?? new string[0]);
|
||||||
|
|
||||||
// Expression values
|
// Expression values
|
||||||
if (message.ContextData?.Count > 0)
|
if (message.ContextData?.Count > 0)
|
||||||
@@ -626,15 +623,15 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpressionValues["secrets"] = Variables.ToSecretsContext();
|
ExpressionValues["secrets"] = Global.Variables.ToSecretsContext();
|
||||||
ExpressionValues["runner"] = new RunnerContext();
|
ExpressionValues["runner"] = new RunnerContext();
|
||||||
ExpressionValues["job"] = new JobContext();
|
ExpressionValues["job"] = new JobContext();
|
||||||
|
|
||||||
Trace.Info("Initialize GitHub context");
|
Trace.Info("Initialize GitHub context");
|
||||||
var githubAccessToken = new StringContextData(Variables.Get("system.github.token"));
|
var githubAccessToken = new StringContextData(Global.Variables.Get("system.github.token"));
|
||||||
var base64EncodedToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{githubAccessToken}"));
|
var base64EncodedToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{githubAccessToken}"));
|
||||||
HostContext.SecretMasker.AddValue(base64EncodedToken);
|
HostContext.SecretMasker.AddValue(base64EncodedToken);
|
||||||
var githubJob = Variables.Get("system.github.job");
|
var githubJob = Global.Variables.Get("system.github.job");
|
||||||
var githubContext = new GitHubContext();
|
var githubContext = new GitHubContext();
|
||||||
githubContext["token"] = githubAccessToken;
|
githubContext["token"] = githubAccessToken;
|
||||||
if (!string.IsNullOrEmpty(githubJob))
|
if (!string.IsNullOrEmpty(githubJob))
|
||||||
@@ -657,10 +654,10 @@ namespace GitHub.Runner.Worker
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Prepend Path
|
// Prepend Path
|
||||||
PrependPath = new List<string>();
|
Global.PrependPath = new List<string>();
|
||||||
|
|
||||||
// JobSteps for job ExecutionContext
|
// JobSteps for job ExecutionContext
|
||||||
JobSteps = new Queue<IStep>();
|
JobSteps = new List<IStep>();
|
||||||
|
|
||||||
// PostJobSteps for job ExecutionContext
|
// PostJobSteps for job ExecutionContext
|
||||||
PostJobSteps = new Stack<IStep>();
|
PostJobSteps = new Stack<IStep>();
|
||||||
@@ -683,10 +680,10 @@ namespace GitHub.Runner.Worker
|
|||||||
_logger.Setup(_mainTimelineId, _record.Id);
|
_logger.Setup(_mainTimelineId, _record.Id);
|
||||||
|
|
||||||
// Initialize 'echo on action command success' property, default to false, unless Step_Debug is set
|
// Initialize 'echo on action command success' property, default to false, unless Step_Debug is set
|
||||||
EchoOnActionCommand = Variables.Step_Debug ?? false;
|
EchoOnActionCommand = Global.Variables.Step_Debug ?? false;
|
||||||
|
|
||||||
// Verbosity (from GitHub.Step_Debug).
|
// Verbosity (from GitHub.Step_Debug).
|
||||||
WriteDebug = Variables.Step_Debug ?? false;
|
Global.WriteDebug = Global.Variables.Step_Debug ?? false;
|
||||||
|
|
||||||
// Hook up JobServerQueueThrottling event, we will log warning on server tarpit.
|
// Hook up JobServerQueueThrottling event, we will log warning on server tarpit.
|
||||||
_jobServerQueue.JobServerQueueThrottling += JobServerQueueThrottling_EventReceived;
|
_jobServerQueue.JobServerQueueThrottling += JobServerQueueThrottling_EventReceived;
|
||||||
@@ -887,6 +884,16 @@ namespace GitHub.Runner.Worker
|
|||||||
// Otherwise individual overloads would need to be implemented (depending on the unit test).
|
// Otherwise individual overloads would need to be implemented (depending on the unit test).
|
||||||
public static class ExecutionContextExtension
|
public static class ExecutionContextExtension
|
||||||
{
|
{
|
||||||
|
public static string GetFullyQualifiedContextName(this IExecutionContext context)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(context.ScopeName))
|
||||||
|
{
|
||||||
|
return $"{context.ScopeName}.{context.ContextName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.ContextName;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Error(this IExecutionContext context, Exception ex)
|
public static void Error(this IExecutionContext context, Exception ex)
|
||||||
{
|
{
|
||||||
context.Error(ex.Message);
|
context.Error(ex.Message);
|
||||||
@@ -925,7 +932,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||||
public static void Debug(this IExecutionContext context, string message)
|
public static void Debug(this IExecutionContext context, string message)
|
||||||
{
|
{
|
||||||
if (context.WriteDebug)
|
if (context.Global.WriteDebug)
|
||||||
{
|
{
|
||||||
var multilines = message?.Replace("\r\n", "\n")?.Split("\n");
|
var multilines = message?.Replace("\r\n", "\n")?.Split("\n");
|
||||||
if (multilines != null)
|
if (multilines != null)
|
||||||
@@ -950,7 +957,7 @@ namespace GitHub.Runner.Worker
|
|||||||
traceWriter = context.ToTemplateTraceWriter();
|
traceWriter = context.ToTemplateTraceWriter();
|
||||||
}
|
}
|
||||||
var schema = PipelineTemplateSchemaFactory.GetSchema();
|
var schema = PipelineTemplateSchemaFactory.GetSchema();
|
||||||
return new PipelineTemplateEvaluator(traceWriter, schema, context.FileTable);
|
return new PipelineTemplateEvaluator(traceWriter, schema, context.Global.FileTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectTemplating.ITraceWriter ToTemplateTraceWriter(this IExecutionContext context)
|
public static ObjectTemplating.ITraceWriter ToTemplateTraceWriter(this IExecutionContext context)
|
||||||
|
|||||||
24
src/Runner.Worker/GlobalContext.cs
Normal file
24
src/Runner.Worker/GlobalContext.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
|
using GitHub.Runner.Worker.Container;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Worker
|
||||||
|
{
|
||||||
|
public sealed class GlobalContext
|
||||||
|
{
|
||||||
|
public ContainerInfo Container { get; set; }
|
||||||
|
public List<ServiceEndpoint> Endpoints { get; set; }
|
||||||
|
public IDictionary<String, String> EnvironmentVariables { get; set; }
|
||||||
|
public PlanFeatures Features { get; set; }
|
||||||
|
public IList<String> FileTable { get; set; }
|
||||||
|
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; set; }
|
||||||
|
public TaskOrchestrationPlanReference Plan { get; set; }
|
||||||
|
public List<string> PrependPath { get; set; }
|
||||||
|
public List<ContainerInfo> ServiceContainers { get; set; }
|
||||||
|
public StepsContext StepsContext { get; set; }
|
||||||
|
public Variables Variables { get; set; }
|
||||||
|
public bool WriteDebug { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
275
src/Runner.Worker/Handlers/CompositeActionHandler.cs
Normal file
275
src/Runner.Worker/Handlers/CompositeActionHandler.cs
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||||
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Worker.Handlers
|
||||||
|
{
|
||||||
|
[ServiceLocator(Default = typeof(CompositeActionHandler))]
|
||||||
|
public interface ICompositeActionHandler : IHandler
|
||||||
|
{
|
||||||
|
CompositeActionExecutionData Data { get; set; }
|
||||||
|
}
|
||||||
|
public sealed class CompositeActionHandler : Handler, ICompositeActionHandler
|
||||||
|
{
|
||||||
|
public CompositeActionExecutionData Data { get; set; }
|
||||||
|
|
||||||
|
public async Task RunAsync(ActionRunStage stage)
|
||||||
|
{
|
||||||
|
// Validate args.
|
||||||
|
Trace.Entering();
|
||||||
|
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
|
||||||
|
ArgUtil.NotNull(Inputs, nameof(Inputs));
|
||||||
|
ArgUtil.NotNull(Data.Steps, nameof(Data.Steps));
|
||||||
|
|
||||||
|
var githubContext = ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
||||||
|
ArgUtil.NotNull(githubContext, nameof(githubContext));
|
||||||
|
|
||||||
|
// Resolve action steps
|
||||||
|
var actionSteps = Data.Steps;
|
||||||
|
|
||||||
|
// Create Context Data to reuse for each composite action step
|
||||||
|
var inputsData = new DictionaryContextData();
|
||||||
|
foreach (var i in Inputs)
|
||||||
|
{
|
||||||
|
inputsData[i.Key] = new StringContextData(i.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Composite Steps List of Steps
|
||||||
|
var compositeSteps = new List<IStep>();
|
||||||
|
|
||||||
|
// Temporary hack until after M271-ish. After M271-ish the server will never send an empty
|
||||||
|
// context name. Generated context names start with "__"
|
||||||
|
var childScopeName = ExecutionContext.GetFullyQualifiedContextName();
|
||||||
|
if (string.IsNullOrEmpty(childScopeName))
|
||||||
|
{
|
||||||
|
childScopeName = $"__{Guid.NewGuid()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Pipelines.ActionStep actionStep in actionSteps)
|
||||||
|
{
|
||||||
|
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||||
|
actionRunner.Action = actionStep;
|
||||||
|
actionRunner.Stage = stage;
|
||||||
|
actionRunner.Condition = actionStep.Condition;
|
||||||
|
|
||||||
|
var step = ExecutionContext.CreateCompositeStep(childScopeName, actionRunner, inputsData, Environment);
|
||||||
|
compositeSteps.Add(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This is where we run each step.
|
||||||
|
await RunStepsAsync(compositeSteps);
|
||||||
|
|
||||||
|
// Get the pointer of the correct "steps" object and pass it to the ExecutionContext so that we can process the outputs correctly
|
||||||
|
ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||||
|
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(ExecutionContext.GetFullyQualifiedContextName());
|
||||||
|
|
||||||
|
ProcessCompositeActionOutputs();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Composite StepRunner should never throw exception out.
|
||||||
|
Trace.Error($"Caught exception from composite steps {nameof(CompositeActionHandler)}: {ex}");
|
||||||
|
ExecutionContext.Error(ex);
|
||||||
|
ExecutionContext.Result = TaskResult.Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCompositeActionOutputs()
|
||||||
|
{
|
||||||
|
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
|
||||||
|
|
||||||
|
// Evaluate the mapped outputs value
|
||||||
|
if (Data.Outputs != null)
|
||||||
|
{
|
||||||
|
// Evaluate the outputs in the steps context to easily retrieve the values
|
||||||
|
var actionManifestManager = HostContext.GetService<IActionManifestManager>();
|
||||||
|
|
||||||
|
// Format ExpressionValues to Dictionary<string, PipelineContextData>
|
||||||
|
var evaluateContext = new Dictionary<string, PipelineContextData>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var pair in ExecutionContext.ExpressionValues)
|
||||||
|
{
|
||||||
|
evaluateContext[pair.Key] = pair.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the evluated composite outputs' values mapped to the outputs named
|
||||||
|
DictionaryContextData actionOutputs = actionManifestManager.EvaluateCompositeOutputs(ExecutionContext, Data.Outputs, evaluateContext);
|
||||||
|
|
||||||
|
// Set the outputs for the outputs object in the whole composite action
|
||||||
|
// Each pair is structured like this
|
||||||
|
// We ignore "description" for now
|
||||||
|
// {
|
||||||
|
// "the-output-name": {
|
||||||
|
// "description": "",
|
||||||
|
// "value": "the value"
|
||||||
|
// },
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
foreach (var pair in actionOutputs)
|
||||||
|
{
|
||||||
|
var outputsName = pair.Key;
|
||||||
|
var outputsAttributes = pair.Value as DictionaryContextData;
|
||||||
|
outputsAttributes.TryGetValue("value", out var val);
|
||||||
|
var outputsValue = val as StringContextData;
|
||||||
|
|
||||||
|
// Set output in the whole composite scope.
|
||||||
|
if (!String.IsNullOrEmpty(outputsName) && !String.IsNullOrEmpty(outputsValue))
|
||||||
|
{
|
||||||
|
ExecutionContext.SetOutput(outputsName, outputsValue, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunStepsAsync(List<IStep> compositeSteps)
|
||||||
|
{
|
||||||
|
ArgUtil.NotNull(compositeSteps, nameof(compositeSteps));
|
||||||
|
|
||||||
|
// The parent StepsRunner of the whole Composite Action Step handles the cancellation stuff already.
|
||||||
|
foreach (IStep step in compositeSteps)
|
||||||
|
{
|
||||||
|
Trace.Info($"Processing composite step: DisplayName='{step.DisplayName}'");
|
||||||
|
|
||||||
|
step.ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
|
||||||
|
|
||||||
|
// Populate env context for each step
|
||||||
|
Trace.Info("Initialize Env context for step");
|
||||||
|
#if OS_WINDOWS
|
||||||
|
var envContext = new DictionaryContextData();
|
||||||
|
#else
|
||||||
|
var envContext = new CaseSensitiveDictionaryContextData();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Global env
|
||||||
|
foreach (var pair in ExecutionContext.Global.EnvironmentVariables)
|
||||||
|
{
|
||||||
|
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stomps over with outside step env
|
||||||
|
if (step.ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
|
||||||
|
{
|
||||||
|
#if OS_WINDOWS
|
||||||
|
var dict = envContextData as DictionaryContextData;
|
||||||
|
#else
|
||||||
|
var dict = envContextData as CaseSensitiveDictionaryContextData;
|
||||||
|
#endif
|
||||||
|
foreach (var pair in dict)
|
||||||
|
{
|
||||||
|
envContext[pair.Key] = pair.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||||
|
|
||||||
|
var actionStep = step as IActionRunner;
|
||||||
|
|
||||||
|
// Set GITHUB_ACTION
|
||||||
|
step.ExecutionContext.SetGitHubContext("action", step.ExecutionContext.GetFullyQualifiedContextName());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Evaluate and merge action's env block to env context
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
|
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, Common.Util.VarUtil.EnvironmentVariableKeyComparer);
|
||||||
|
foreach (var env in actionEnvironment)
|
||||||
|
{
|
||||||
|
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// fail the step since there is an evaluate error.
|
||||||
|
Trace.Info("Caught exception in Composite Steps Runner from expression for step.env");
|
||||||
|
// evaluateStepEnvFailed = true;
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
step.ExecutionContext.Complete(TaskResult.Failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
await RunStepAsync(step);
|
||||||
|
|
||||||
|
// Directly after the step, check if the step has failed or cancelled
|
||||||
|
// If so, return that to the output
|
||||||
|
if (step.ExecutionContext.Result == TaskResult.Failed || step.ExecutionContext.Result == TaskResult.Canceled)
|
||||||
|
{
|
||||||
|
ExecutionContext.Result = step.ExecutionContext.Result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add compat for other types of steps.
|
||||||
|
}
|
||||||
|
// Completion Status handled by StepsRunner for the whole Composite Action Step
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunStepAsync(IStep step)
|
||||||
|
{
|
||||||
|
// Try to evaluate the display name
|
||||||
|
if (step is IActionRunner actionRunner && actionRunner.Stage == ActionRunStage.Main)
|
||||||
|
{
|
||||||
|
actionRunner.TryEvaluateDisplayName(step.ExecutionContext.ExpressionValues, step.ExecutionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the step.
|
||||||
|
Trace.Info("Starting the step.");
|
||||||
|
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
|
||||||
|
|
||||||
|
// TODO: Fix for Step Level Timeout Attributes for an individual Composite Run Step
|
||||||
|
// For now, we are not going to support this for an individual composite run step
|
||||||
|
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
|
|
||||||
|
await Common.Util.EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await step.RunAsync();
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException ex)
|
||||||
|
{
|
||||||
|
if (step.ExecutionContext.CancellationToken.IsCancellationRequested &&
|
||||||
|
!ExecutionContext.Root.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Trace.Error($"Caught timeout exception from step: {ex.Message}");
|
||||||
|
step.ExecutionContext.Error("The action has timed out.");
|
||||||
|
step.ExecutionContext.Result = TaskResult.Failed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Trace.Error($"Caught cancellation exception from step: {ex}");
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
step.ExecutionContext.Result = TaskResult.Canceled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log the error and fail the step.
|
||||||
|
Trace.Error($"Caught exception from step: {ex}");
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
step.ExecutionContext.Result = TaskResult.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge execution context result with command result
|
||||||
|
if (step.ExecutionContext.CommandResult != null)
|
||||||
|
{
|
||||||
|
step.ExecutionContext.Result = Common.Util.TaskResultUtil.MergeTaskResults(step.ExecutionContext.Result, step.ExecutionContext.CommandResult.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Trace.Info($"Step result: {step.ExecutionContext.Result}");
|
||||||
|
|
||||||
|
// Complete the step context.
|
||||||
|
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,8 +49,9 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
// ensure docker file exist
|
// ensure docker file exist
|
||||||
var dockerFile = Path.Combine(ActionDirectory, Data.Image);
|
var dockerFile = Path.Combine(ActionDirectory, Data.Image);
|
||||||
ArgUtil.File(dockerFile, nameof(Data.Image));
|
ArgUtil.File(dockerFile, nameof(Data.Image));
|
||||||
ExecutionContext.Output($"Dockerfile for action: '{dockerFile}'.");
|
|
||||||
|
|
||||||
|
ExecutionContext.Output($"##[group]Building docker image");
|
||||||
|
ExecutionContext.Output($"Dockerfile for action: '{dockerFile}'.");
|
||||||
var imageName = $"{dockerManger.DockerInstanceLabel}:{ExecutionContext.Id.ToString("N")}";
|
var imageName = $"{dockerManger.DockerInstanceLabel}:{ExecutionContext.Id.ToString("N")}";
|
||||||
var buildExitCode = await dockerManger.DockerBuild(
|
var buildExitCode = await dockerManger.DockerBuild(
|
||||||
ExecutionContext,
|
ExecutionContext,
|
||||||
@@ -58,6 +59,8 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
dockerFile,
|
dockerFile,
|
||||||
Directory.GetParent(dockerFile).FullName,
|
Directory.GetParent(dockerFile).FullName,
|
||||||
imageName);
|
imageName);
|
||||||
|
ExecutionContext.Output("##[endgroup]");
|
||||||
|
|
||||||
if (buildExitCode != 0)
|
if (buildExitCode != 0)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Docker build failed with exit code {buildExitCode}");
|
throw new InvalidOperationException($"Docker build failed with exit code {buildExitCode}");
|
||||||
@@ -185,7 +188,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add Actions Runtime server info
|
// Add Actions Runtime server info
|
||||||
var systemConnection = ExecutionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
var systemConnection = ExecutionContext.Global.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
Environment["ACTIONS_RUNTIME_URL"] = systemConnection.Url.AbsoluteUri;
|
Environment["ACTIONS_RUNTIME_URL"] = systemConnection.Url.AbsoluteUri;
|
||||||
Environment["ACTIONS_RUNTIME_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
|
Environment["ACTIONS_RUNTIME_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
|
||||||
if (systemConnection.Data.TryGetValue("CacheServerUrl", out var cacheUrl) && !string.IsNullOrEmpty(cacheUrl))
|
if (systemConnection.Data.TryGetValue("CacheServerUrl", out var cacheUrl) && !string.IsNullOrEmpty(cacheUrl))
|
||||||
|
|||||||
@@ -148,14 +148,14 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
// Validate args.
|
// Validate args.
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
ArgUtil.NotNull(ExecutionContext.PrependPath, nameof(ExecutionContext.PrependPath));
|
ArgUtil.NotNull(ExecutionContext.Global.PrependPath, nameof(ExecutionContext.Global.PrependPath));
|
||||||
if (ExecutionContext.PrependPath.Count == 0)
|
if (ExecutionContext.Global.PrependPath.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepend path.
|
// Prepend path.
|
||||||
string prepend = string.Join(Path.PathSeparator.ToString(), ExecutionContext.PrependPath.Reverse<string>());
|
string prepend = string.Join(Path.PathSeparator.ToString(), ExecutionContext.Global.PrependPath.Reverse<string>());
|
||||||
var containerStepHost = StepHost as ContainerStepHost;
|
var containerStepHost = StepHost as ContainerStepHost;
|
||||||
if (containerStepHost != null)
|
if (containerStepHost != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
handler = HostContext.CreateService<IRunnerPluginHandler>();
|
handler = HostContext.CreateService<IRunnerPluginHandler>();
|
||||||
(handler as IRunnerPluginHandler).Data = data as PluginActionExecutionData;
|
(handler as IRunnerPluginHandler).Data = data as PluginActionExecutionData;
|
||||||
}
|
}
|
||||||
|
else if (data.ExecutionType == ActionExecutionType.Composite)
|
||||||
|
{
|
||||||
|
handler = HostContext.CreateService<ICompositeActionHandler>();
|
||||||
|
(handler as ICompositeActionHandler).Data = data as CompositeActionExecutionData;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add Actions Runtime server info
|
// Add Actions Runtime server info
|
||||||
var systemConnection = ExecutionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
var systemConnection = ExecutionContext.Global.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
Environment["ACTIONS_RUNTIME_URL"] = systemConnection.Url.AbsoluteUri;
|
Environment["ACTIONS_RUNTIME_URL"] = systemConnection.Url.AbsoluteUri;
|
||||||
Environment["ACTIONS_RUNTIME_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
|
Environment["ACTIONS_RUNTIME_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
|
||||||
if (systemConnection.Data.TryGetValue("CacheServerUrl", out var cacheUrl) && !string.IsNullOrEmpty(cacheUrl))
|
if (systemConnection.Data.TryGetValue("CacheServerUrl", out var cacheUrl) && !string.IsNullOrEmpty(cacheUrl))
|
||||||
@@ -82,6 +82,10 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
|
|
||||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext);
|
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext);
|
||||||
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||||
|
if (System.Environment.GetEnvironmentVariable("K8S_POD_NAME") != null)
|
||||||
|
{
|
||||||
|
file = Path.Combine(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), "__externals_copy"), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||||
|
}
|
||||||
|
|
||||||
// Format the arguments passed to node.
|
// Format the arguments passed to node.
|
||||||
// 1) Wrap the script file path in double quotes.
|
// 1) Wrap the script file path in double quotes.
|
||||||
@@ -113,7 +117,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
requireExitCodeZero: false,
|
requireExitCodeZero: false,
|
||||||
outputEncoding: outputEncoding,
|
outputEncoding: outputEncoding,
|
||||||
killProcessOnCancel: false,
|
killProcessOnCancel: false,
|
||||||
inheritConsoleHandler: !ExecutionContext.Variables.Retain_Default_Encoding,
|
inheritConsoleHandler: !ExecutionContext.Global.Variables.Retain_Default_Encoding,
|
||||||
cancellationToken: ExecutionContext.CancellationToken);
|
cancellationToken: ExecutionContext.CancellationToken);
|
||||||
|
|
||||||
// Wait for either the node exit or force finish through ##vso command
|
// Wait for either the node exit or force finish through ##vso command
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
_executionContext = executionContext;
|
_executionContext = executionContext;
|
||||||
_commandManager = commandManager;
|
_commandManager = commandManager;
|
||||||
_container = container ?? executionContext.Container;
|
_container = container ?? executionContext.Global.Container;
|
||||||
|
|
||||||
// Recursion failsafe (test override)
|
// Recursion failsafe (test override)
|
||||||
var failsafeString = Environment.GetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE");
|
var failsafeString = Environment.GetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE");
|
||||||
@@ -41,7 +41,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine the timeout
|
// Determine the timeout
|
||||||
var timeoutStr = _executionContext.Variables.Get(_timeoutKey);
|
var timeoutStr = _executionContext.Global.Variables.Get(_timeoutKey);
|
||||||
if (string.IsNullOrEmpty(timeoutStr) ||
|
if (string.IsNullOrEmpty(timeoutStr) ||
|
||||||
!TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) ||
|
!TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) ||
|
||||||
_timeout <= TimeSpan.Zero)
|
_timeout <= TimeSpan.Zero)
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
string shellCommand;
|
string shellCommand;
|
||||||
string shellCommandPath = null;
|
string shellCommandPath = null;
|
||||||
bool validateShellOnHost = !(StepHost is ContainerStepHost);
|
bool validateShellOnHost = !(StepHost is ContainerStepHost);
|
||||||
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.PrependPath.Reverse<string>());
|
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.Global.PrependPath.Reverse<string>());
|
||||||
string shell = null;
|
string shell = null;
|
||||||
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
||||||
{
|
{
|
||||||
// TODO: figure out how defaults interact with template later
|
// TODO: figure out how defaults interact with template later
|
||||||
// for now, we won't check job.defaults if we are inside a template.
|
// for now, we won't check job.defaults if we are inside a template.
|
||||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||||
{
|
{
|
||||||
runDefaults.TryGetValue("shell", out shell);
|
runDefaults.TryGetValue("shell", out shell);
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
// TODO: figure out how defaults interact with template later
|
// TODO: figure out how defaults interact with template later
|
||||||
// for now, we won't check job.defaults if we are inside a template.
|
// for now, we won't check job.defaults if we are inside a template.
|
||||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||||
{
|
{
|
||||||
if (runDefaults.TryGetValue("working-directory", out workingDirectory))
|
if (runDefaults.TryGetValue("working-directory", out workingDirectory))
|
||||||
{
|
{
|
||||||
@@ -169,7 +169,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
// TODO: figure out how defaults interact with template later
|
// TODO: figure out how defaults interact with template later
|
||||||
// for now, we won't check job.defaults if we are inside a template.
|
// for now, we won't check job.defaults if we are inside a template.
|
||||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.JobDefaults.TryGetValue("run", out var runDefaults))
|
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||||
{
|
{
|
||||||
if (runDefaults.TryGetValue("shell", out shell))
|
if (runDefaults.TryGetValue("shell", out shell))
|
||||||
{
|
{
|
||||||
@@ -180,7 +180,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
|
|
||||||
var isContainerStepHost = StepHost is ContainerStepHost;
|
var isContainerStepHost = StepHost is ContainerStepHost;
|
||||||
|
|
||||||
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.PrependPath.Reverse<string>());
|
string prependPath = string.Join(Path.PathSeparator.ToString(), ExecutionContext.Global.PrependPath.Reverse<string>());
|
||||||
string commandPath, argFormat, shellCommand;
|
string commandPath, argFormat, shellCommand;
|
||||||
// Set up default command and arguments
|
// Set up default command and arguments
|
||||||
if (string.IsNullOrEmpty(shell))
|
if (string.IsNullOrEmpty(shell))
|
||||||
@@ -232,7 +232,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
// Normalize Windows line endings
|
// Normalize Windows line endings
|
||||||
contents = contents.Replace("\r\n", "\n").Replace("\n", "\r\n");
|
contents = contents.Replace("\r\n", "\n").Replace("\n", "\r\n");
|
||||||
var encoding = ExecutionContext.Variables.Retain_Default_Encoding && Console.InputEncoding.CodePage != 65001
|
var encoding = ExecutionContext.Global.Variables.Retain_Default_Encoding && Console.InputEncoding.CodePage != 65001
|
||||||
? Console.InputEncoding
|
? Console.InputEncoding
|
||||||
: new UTF8Encoding(false);
|
: new UTF8Encoding(false);
|
||||||
#else
|
#else
|
||||||
@@ -285,7 +285,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
requireExitCodeZero: false,
|
requireExitCodeZero: false,
|
||||||
outputEncoding: null,
|
outputEncoding: null,
|
||||||
killProcessOnCancel: false,
|
killProcessOnCancel: false,
|
||||||
inheritConsoleHandler: !ExecutionContext.Variables.Retain_Default_Encoding,
|
inheritConsoleHandler: !ExecutionContext.Global.Variables.Retain_Default_Encoding,
|
||||||
cancellationToken: ExecutionContext.CancellationToken);
|
cancellationToken: ExecutionContext.CancellationToken);
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace GitHub.Runner.Worker
|
|||||||
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, jobContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, jobContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||||
foreach (var pair in environmentVariables)
|
foreach (var pair in environmentVariables)
|
||||||
{
|
{
|
||||||
context.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty;
|
context.Global.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty;
|
||||||
context.SetEnvContext(pair.Key, pair.Value ?? string.Empty);
|
context.SetEnvContext(pair.Key, pair.Value ?? string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ namespace GitHub.Runner.Worker
|
|||||||
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||||
if (container != null)
|
if (container != null)
|
||||||
{
|
{
|
||||||
jobContext.Container = new Container.ContainerInfo(HostContext, container);
|
jobContext.Global.Container = new Container.ContainerInfo(HostContext, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the job service containers
|
// Evaluate the job service containers
|
||||||
@@ -184,7 +184,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
var networkAlias = pair.Key;
|
var networkAlias = pair.Key;
|
||||||
var serviceContainer = pair.Value;
|
var serviceContainer = pair.Value;
|
||||||
jobContext.ServiceContainers.Add(new Container.ContainerInfo(HostContext, serviceContainer, false, networkAlias));
|
jobContext.Global.ServiceContainers.Add(new Container.ContainerInfo(HostContext, serviceContainer, false, networkAlias));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,14 +195,14 @@ namespace GitHub.Runner.Worker
|
|||||||
var defaults = token.AssertMapping("defaults");
|
var defaults = token.AssertMapping("defaults");
|
||||||
if (defaults.Any(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase)))
|
if (defaults.Any(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
context.JobDefaults["run"] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
context.Global.JobDefaults["run"] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
var defaultsRun = defaults.First(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase));
|
var defaultsRun = defaults.First(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase));
|
||||||
var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues, jobContext.ExpressionFunctions);
|
||||||
foreach (var pair in jobDefaults)
|
foreach (var pair in jobDefaults)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(pair.Value))
|
if (!string.IsNullOrEmpty(pair.Value))
|
||||||
{
|
{
|
||||||
context.JobDefaults["run"][pair.Key] = pair.Value;
|
context.Global.JobDefaults["run"][pair.Key] = pair.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,15 +216,15 @@ namespace GitHub.Runner.Worker
|
|||||||
preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
|
preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
|
||||||
|
|
||||||
// Add start-container steps, record and stop-container steps
|
// Add start-container steps, record and stop-container steps
|
||||||
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
|
if (jobContext.Global.Container != null || jobContext.Global.ServiceContainers.Count > 0)
|
||||||
{
|
{
|
||||||
var containerProvider = HostContext.GetService<IContainerOperationProvider>();
|
var containerProvider = HostContext.GetService<IContainerOperationProvider>();
|
||||||
var containers = new List<Container.ContainerInfo>();
|
var containers = new List<Container.ContainerInfo>();
|
||||||
if (jobContext.Container != null)
|
if (jobContext.Global.Container != null)
|
||||||
{
|
{
|
||||||
containers.Add(jobContext.Container);
|
containers.Add(jobContext.Global.Container);
|
||||||
}
|
}
|
||||||
containers.AddRange(jobContext.ServiceContainers);
|
containers.AddRange(jobContext.Global.ServiceContainers);
|
||||||
|
|
||||||
preJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync,
|
preJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync,
|
||||||
condition: $"{PipelineTemplateConstants.Success}()",
|
condition: $"{PipelineTemplateConstants.Success}()",
|
||||||
@@ -296,7 +296,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
ArgUtil.NotNull(actionStep, step.DisplayName);
|
ArgUtil.NotNull(actionStep, step.DisplayName);
|
||||||
intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState);
|
intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState);
|
||||||
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionState);
|
actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, null, actionStep.Action.ContextName, intraActionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +305,7 @@ namespace GitHub.Runner.Worker
|
|||||||
steps.AddRange(jobSteps);
|
steps.AddRange(jobSteps);
|
||||||
|
|
||||||
// Prepare for orphan process cleanup
|
// Prepare for orphan process cleanup
|
||||||
_processCleanup = jobContext.Variables.GetBoolean("process.clean") ?? true;
|
_processCleanup = jobContext.Global.Variables.GetBoolean("process.clean") ?? true;
|
||||||
if (_processCleanup)
|
if (_processCleanup)
|
||||||
{
|
{
|
||||||
// Set the RUNNER_TRACKING_ID env variable.
|
// Set the RUNNER_TRACKING_ID env variable.
|
||||||
@@ -376,13 +376,13 @@ namespace GitHub.Runner.Worker
|
|||||||
var envContext = new CaseSensitiveDictionaryContextData();
|
var envContext = new CaseSensitiveDictionaryContextData();
|
||||||
#endif
|
#endif
|
||||||
context.ExpressionValues["env"] = envContext;
|
context.ExpressionValues["env"] = envContext;
|
||||||
foreach (var pair in context.EnvironmentVariables)
|
foreach (var pair in context.Global.EnvironmentVariables)
|
||||||
{
|
{
|
||||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Trace.Info("Initialize steps context for evaluating job outputs");
|
Trace.Info("Initialize steps context for evaluating job outputs");
|
||||||
context.ExpressionValues["steps"] = context.StepsContext.GetScope(context.ScopeName);
|
context.ExpressionValues["steps"] = context.Global.StepsContext.GetScope(context.ScopeName);
|
||||||
|
|
||||||
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
var templateEvaluator = context.ToPipelineTemplateEvaluator();
|
||||||
var outputs = templateEvaluator.EvaluateJobOutput(message.JobOutputs, context.ExpressionValues, context.ExpressionFunctions);
|
var outputs = templateEvaluator.EvaluateJobOutput(message.JobOutputs, context.ExpressionValues, context.ExpressionFunctions);
|
||||||
@@ -413,7 +413,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Variables.GetBoolean(Constants.Variables.Actions.RunnerDebug) ?? false)
|
if (context.Global.Variables.GetBoolean(Constants.Variables.Actions.RunnerDebug) ?? false)
|
||||||
{
|
{
|
||||||
Trace.Info("Support log upload starting.");
|
Trace.Info("Support log upload starting.");
|
||||||
context.Output("Uploading runner diagnostic logs");
|
context.Output("Uploading runner diagnostic logs");
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace GitHub.Runner.Worker
|
|||||||
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed);
|
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jobContext.WriteDebug)
|
if (jobContext.Global.WriteDebug)
|
||||||
{
|
{
|
||||||
jobContext.SetRunnerContext("debug", "1");
|
jobContext.SetRunnerContext("debug", "1");
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var step in jobSteps)
|
foreach (var step in jobSteps)
|
||||||
{
|
{
|
||||||
jobContext.JobSteps.Enqueue(step);
|
jobContext.JobSteps.Add(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
await stepsRunner.RunAsync(jobContext);
|
await stepsRunner.RunAsync(jobContext);
|
||||||
@@ -209,7 +209,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Clean TEMP after finish process jobserverqueue, since there might be a pending fileupload still use the TEMP dir.
|
// Clean TEMP after finish process jobserverqueue, since there might be a pending fileupload still use the TEMP dir.
|
||||||
_tempDirectoryManager?.CleanupTempDirectory();
|
_tempDirectoryManager?.CleanupTempDirectory();
|
||||||
|
|
||||||
if (!jobContext.Features.HasFlag(PlanFeatures.JobCompletedPlanEvent))
|
if (!jobContext.Global.Features.HasFlag(PlanFeatures.JobCompletedPlanEvent))
|
||||||
{
|
{
|
||||||
Trace.Info($"Skip raise job completed event call from worker because Plan version is {message.Plan.Version}");
|
Trace.Info($"Skip raise job completed event call from worker because Plan version is {message.Plan.Version}");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -100,12 +100,12 @@ namespace GitHub.Runner.Worker
|
|||||||
RunnerActionPluginExecutionContext pluginContext = new RunnerActionPluginExecutionContext
|
RunnerActionPluginExecutionContext pluginContext = new RunnerActionPluginExecutionContext
|
||||||
{
|
{
|
||||||
Inputs = inputs,
|
Inputs = inputs,
|
||||||
Endpoints = context.Endpoints,
|
Endpoints = context.Global.Endpoints,
|
||||||
Context = context.ExpressionValues
|
Context = context.ExpressionValues
|
||||||
};
|
};
|
||||||
|
|
||||||
// variables
|
// variables
|
||||||
foreach (var variable in context.Variables.AllVariables)
|
foreach (var variable in context.Global.Variables.AllVariables)
|
||||||
{
|
{
|
||||||
pluginContext.Variables[variable.Name] = new VariableValue(variable.Value, variable.Secret);
|
pluginContext.Variables[variable.Name] = new VariableValue(variable.Value, variable.Secret);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,18 +59,19 @@ namespace GitHub.Runner.Worker
|
|||||||
checkPostJobActions = true;
|
checkPostJobActions = true;
|
||||||
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
||||||
{
|
{
|
||||||
jobContext.JobSteps.Enqueue(postStep);
|
jobContext.JobSteps.Add(postStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var step = jobContext.JobSteps.Dequeue();
|
var step = jobContext.JobSteps[0];
|
||||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps.Peek() : null;
|
jobContext.JobSteps.RemoveAt(0);
|
||||||
|
|
||||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||||
ArgUtil.NotNull(step.ExecutionContext.Variables, nameof(step.ExecutionContext.Variables));
|
ArgUtil.NotNull(step.ExecutionContext.Global, nameof(step.ExecutionContext.Global));
|
||||||
|
ArgUtil.NotNull(step.ExecutionContext.Global.Variables, nameof(step.ExecutionContext.Global.Variables));
|
||||||
|
|
||||||
// Start
|
// Start
|
||||||
step.ExecutionContext.Start();
|
step.ExecutionContext.Start();
|
||||||
@@ -82,155 +83,165 @@ namespace GitHub.Runner.Worker
|
|||||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
|
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
|
||||||
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<HashFilesFunction>(PipelineTemplateConstants.HashFiles, 1, byte.MaxValue));
|
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<HashFilesFunction>(PipelineTemplateConstants.HashFiles, 1, byte.MaxValue));
|
||||||
|
|
||||||
// Initialize scope
|
step.ExecutionContext.ExpressionValues["steps"] = step.ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
|
||||||
if (InitializeScope(step, scopeInputs))
|
|
||||||
{
|
// Populate env context for each step
|
||||||
// Populate env context for each step
|
Trace.Info("Initialize Env context for step");
|
||||||
Trace.Info("Initialize Env context for step");
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
var envContext = new DictionaryContextData();
|
var envContext = new DictionaryContextData();
|
||||||
#else
|
#else
|
||||||
var envContext = new CaseSensitiveDictionaryContextData();
|
var envContext = new CaseSensitiveDictionaryContextData();
|
||||||
#endif
|
#endif
|
||||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
|
||||||
foreach (var pair in step.ExecutionContext.EnvironmentVariables)
|
// Global env
|
||||||
|
foreach (var pair in step.ExecutionContext.Global.EnvironmentVariables)
|
||||||
|
{
|
||||||
|
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||||
|
|
||||||
|
bool evaluateStepEnvFailed = false;
|
||||||
|
if (step is IActionRunner actionStep)
|
||||||
|
{
|
||||||
|
// Set GITHUB_ACTION
|
||||||
|
// Warning: Do not turn on FF DistributedTask.UseContextNameForGITHUBACTION until after M271-ish. After M271-ish
|
||||||
|
// the server will never send an empty context name. Generated context names start with "__"
|
||||||
|
if (step.ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseContextNameForGITHUBACTION") ?? false)
|
||||||
{
|
{
|
||||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
step.ExecutionContext.SetGitHubContext("action", step.ExecutionContext.GetFullyQualifiedContextName());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool evaluateStepEnvFailed = false;
|
try
|
||||||
if (step is IActionRunner actionStep)
|
|
||||||
{
|
{
|
||||||
// Set GITHUB_ACTION
|
// Evaluate and merge action's env block to env context
|
||||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
|
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
||||||
try
|
foreach (var env in actionEnvironment)
|
||||||
{
|
{
|
||||||
// Evaluate and merge action's env block to env context
|
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
|
||||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
|
}
|
||||||
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
|
}
|
||||||
foreach (var env in actionEnvironment)
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// fail the step since there is an evaluate error.
|
||||||
|
Trace.Info("Caught exception from expression for step.env");
|
||||||
|
evaluateStepEnvFailed = true;
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
CompleteStep(step, TaskResult.Failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!evaluateStepEnvFailed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Register job cancellation call back only if job cancellation token not been fire before each step run
|
||||||
|
if (!jobContext.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// Test the condition again. The job was canceled after the condition was originally evaluated.
|
||||||
|
jobCancelRegister = jobContext.CancellationToken.Register(() =>
|
||||||
{
|
{
|
||||||
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
|
// mark job as cancelled
|
||||||
|
jobContext.Result = TaskResult.Canceled;
|
||||||
|
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
||||||
|
|
||||||
|
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
|
||||||
|
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
|
||||||
|
var conditionReTestResult = false;
|
||||||
|
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionReTestTraceWriter);
|
||||||
|
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||||
|
conditionReTestResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Cancel the step since we get exception while re-evaluate step condition.
|
||||||
|
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
||||||
|
step.ExecutionContext.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conditionReTestResult)
|
||||||
|
{
|
||||||
|
// Cancel the step.
|
||||||
|
Trace.Info("Cancel current running step.");
|
||||||
|
step.ExecutionContext.CancelToken();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (jobContext.Result != TaskResult.Canceled)
|
||||||
|
{
|
||||||
|
// mark job as cancelled
|
||||||
|
jobContext.Result = TaskResult.Canceled;
|
||||||
|
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
// Evaluate condition.
|
||||||
|
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
||||||
|
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
||||||
|
var conditionResult = false;
|
||||||
|
var conditionEvaluateError = default(Exception);
|
||||||
|
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
||||||
|
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
||||||
|
conditionResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Info("Caught exception from expression.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
conditionEvaluateError = ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no evaluate error but condition is false
|
||||||
|
if (!conditionResult && conditionEvaluateError == null)
|
||||||
|
{
|
||||||
|
// Condition == false
|
||||||
|
Trace.Info("Skipping step due to condition evaluation.");
|
||||||
|
CompleteStep(step, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
|
||||||
|
}
|
||||||
|
else if (conditionEvaluateError != null)
|
||||||
{
|
{
|
||||||
// fail the step since there is an evaluate error.
|
// fail the step since there is an evaluate error.
|
||||||
Trace.Info("Caught exception from expression for step.env");
|
step.ExecutionContext.Error(conditionEvaluateError);
|
||||||
evaluateStepEnvFailed = true;
|
CompleteStep(step, TaskResult.Failed);
|
||||||
step.ExecutionContext.Error(ex);
|
}
|
||||||
CompleteStep(step, nextStep, TaskResult.Failed);
|
else
|
||||||
|
{
|
||||||
|
// Run the step.
|
||||||
|
await RunStepAsync(step, jobContext.CancellationToken);
|
||||||
|
CompleteStep(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
if (!evaluateStepEnvFailed)
|
|
||||||
{
|
{
|
||||||
try
|
if (jobCancelRegister != null)
|
||||||
{
|
{
|
||||||
// Register job cancellation call back only if job cancellation token not been fire before each step run
|
jobCancelRegister?.Dispose();
|
||||||
if (!jobContext.CancellationToken.IsCancellationRequested)
|
jobCancelRegister = null;
|
||||||
{
|
|
||||||
// Test the condition again. The job was canceled after the condition was originally evaluated.
|
|
||||||
jobCancelRegister = jobContext.CancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
// mark job as cancelled
|
|
||||||
jobContext.Result = TaskResult.Canceled;
|
|
||||||
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
|
||||||
|
|
||||||
step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'.");
|
|
||||||
var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only
|
|
||||||
var conditionReTestResult = false;
|
|
||||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
step.ExecutionContext.Debug($"Skip Re-evaluate condition on runner shutdown.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionReTestTraceWriter);
|
|
||||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
|
||||||
conditionReTestResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Cancel the step since we get exception while re-evaluate step condition.
|
|
||||||
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
|
|
||||||
step.ExecutionContext.Error(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conditionReTestResult)
|
|
||||||
{
|
|
||||||
// Cancel the step.
|
|
||||||
Trace.Info("Cancel current running step.");
|
|
||||||
step.ExecutionContext.CancelToken();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (jobContext.Result != TaskResult.Canceled)
|
|
||||||
{
|
|
||||||
// mark job as cancelled
|
|
||||||
jobContext.Result = TaskResult.Canceled;
|
|
||||||
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate condition.
|
|
||||||
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
|
|
||||||
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
|
|
||||||
var conditionResult = false;
|
|
||||||
var conditionEvaluateError = default(Exception);
|
|
||||||
if (HostContext.RunnerShutdownToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
step.ExecutionContext.Debug($"Skip evaluate condition on runner shutdown.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
|
||||||
var condition = new BasicExpressionToken(null, null, null, step.Condition);
|
|
||||||
conditionResult = templateEvaluator.EvaluateStepIf(condition, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, step.ExecutionContext.ToExpressionState());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Info("Caught exception from expression.");
|
|
||||||
Trace.Error(ex);
|
|
||||||
conditionEvaluateError = ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no evaluate error but condition is false
|
|
||||||
if (!conditionResult && conditionEvaluateError == null)
|
|
||||||
{
|
|
||||||
// Condition == false
|
|
||||||
Trace.Info("Skipping step due to condition evaluation.");
|
|
||||||
CompleteStep(step, nextStep, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
|
|
||||||
}
|
|
||||||
else if (conditionEvaluateError != null)
|
|
||||||
{
|
|
||||||
// fail the step since there is an evaluate error.
|
|
||||||
step.ExecutionContext.Error(conditionEvaluateError);
|
|
||||||
CompleteStep(step, nextStep, TaskResult.Failed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Run the step.
|
|
||||||
await RunStepAsync(step, jobContext.CancellationToken);
|
|
||||||
CompleteStep(step, nextStep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (jobCancelRegister != null)
|
|
||||||
{
|
|
||||||
jobCancelRegister?.Dispose();
|
|
||||||
jobCancelRegister = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,40 +296,7 @@ namespace GitHub.Runner.Worker
|
|||||||
step.ExecutionContext.SetTimeout(timeout);
|
step.ExecutionContext.SetTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OS_WINDOWS
|
await EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Console.InputEncoding.CodePage != 65001)
|
|
||||||
{
|
|
||||||
using (var p = HostContext.CreateService<IProcessInvoker>())
|
|
||||||
{
|
|
||||||
// Use UTF8 code page
|
|
||||||
int exitCode = await p.ExecuteAsync(workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Work),
|
|
||||||
fileName: WhichUtil.Which("chcp", true, Trace),
|
|
||||||
arguments: "65001",
|
|
||||||
environment: null,
|
|
||||||
requireExitCodeZero: false,
|
|
||||||
outputEncoding: null,
|
|
||||||
killProcessOnCancel: false,
|
|
||||||
redirectStandardIn: null,
|
|
||||||
inheritConsoleHandler: true,
|
|
||||||
cancellationToken: step.ExecutionContext.CancellationToken);
|
|
||||||
if (exitCode == 0)
|
|
||||||
{
|
|
||||||
Trace.Info("Successfully returned to code page 65001 (UTF8)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Trace.Warning($"'chcp 65001' failed with exit code {exitCode}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Warning($"'chcp 65001' failed with exception {ex.Message}");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -384,117 +362,9 @@ namespace GitHub.Runner.Worker
|
|||||||
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
|
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool InitializeScope(IStep step, Dictionary<string, PipelineContextData> scopeInputs)
|
private void CompleteStep(IStep step, TaskResult? result = null, string resultCode = null)
|
||||||
{
|
{
|
||||||
var executionContext = step.ExecutionContext;
|
var executionContext = step.ExecutionContext;
|
||||||
var stepsContext = executionContext.StepsContext;
|
|
||||||
if (!string.IsNullOrEmpty(executionContext.ScopeName))
|
|
||||||
{
|
|
||||||
// Gather uninitialized current and ancestor scopes
|
|
||||||
var scope = executionContext.Scopes[executionContext.ScopeName];
|
|
||||||
var scopesToInitialize = default(Stack<ContextScope>);
|
|
||||||
while (scope != null && !scopeInputs.ContainsKey(scope.Name))
|
|
||||||
{
|
|
||||||
if (scopesToInitialize == null)
|
|
||||||
{
|
|
||||||
scopesToInitialize = new Stack<ContextScope>();
|
|
||||||
}
|
|
||||||
scopesToInitialize.Push(scope);
|
|
||||||
scope = string.IsNullOrEmpty(scope.ParentName) ? null : executionContext.Scopes[scope.ParentName];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize current and ancestor scopes
|
|
||||||
while (scopesToInitialize?.Count > 0)
|
|
||||||
{
|
|
||||||
scope = scopesToInitialize.Pop();
|
|
||||||
executionContext.Debug($"Initializing scope '{scope.Name}'");
|
|
||||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scope.ParentName);
|
|
||||||
executionContext.ExpressionValues["inputs"] = !String.IsNullOrEmpty(scope.ParentName) ? scopeInputs[scope.ParentName] : null;
|
|
||||||
var templateEvaluator = executionContext.ToPipelineTemplateEvaluator();
|
|
||||||
var inputs = default(DictionaryContextData);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
inputs = templateEvaluator.EvaluateStepScopeInputs(scope.Inputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Info($"Caught exception from initialize scope '{scope.Name}'");
|
|
||||||
Trace.Error(ex);
|
|
||||||
executionContext.Error(ex);
|
|
||||||
executionContext.Complete(TaskResult.Failed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
scopeInputs[scope.Name] = inputs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup expression values
|
|
||||||
var scopeName = executionContext.ScopeName;
|
|
||||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scopeName);
|
|
||||||
executionContext.ExpressionValues["inputs"] = string.IsNullOrEmpty(scopeName) ? null : scopeInputs[scopeName];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CompleteStep(IStep step, IStep nextStep, TaskResult? result = null, string resultCode = null)
|
|
||||||
{
|
|
||||||
var executionContext = step.ExecutionContext;
|
|
||||||
if (!string.IsNullOrEmpty(executionContext.ScopeName))
|
|
||||||
{
|
|
||||||
// Gather current and ancestor scopes to finalize
|
|
||||||
var scope = executionContext.Scopes[executionContext.ScopeName];
|
|
||||||
var scopesToFinalize = default(Queue<ContextScope>);
|
|
||||||
var nextStepScopeName = nextStep?.ExecutionContext.ScopeName;
|
|
||||||
while (scope != null &&
|
|
||||||
!string.Equals(nextStepScopeName, scope.Name, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
!(nextStepScopeName ?? string.Empty).StartsWith($"{scope.Name}.", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (scopesToFinalize == null)
|
|
||||||
{
|
|
||||||
scopesToFinalize = new Queue<ContextScope>();
|
|
||||||
}
|
|
||||||
scopesToFinalize.Enqueue(scope);
|
|
||||||
scope = string.IsNullOrEmpty(scope.ParentName) ? null : executionContext.Scopes[scope.ParentName];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalize current and ancestor scopes
|
|
||||||
var stepsContext = step.ExecutionContext.StepsContext;
|
|
||||||
while (scopesToFinalize?.Count > 0)
|
|
||||||
{
|
|
||||||
scope = scopesToFinalize.Dequeue();
|
|
||||||
executionContext.Debug($"Finalizing scope '{scope.Name}'");
|
|
||||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scope.Name);
|
|
||||||
executionContext.ExpressionValues["inputs"] = null;
|
|
||||||
var templateEvaluator = executionContext.ToPipelineTemplateEvaluator();
|
|
||||||
var outputs = default(DictionaryContextData);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
outputs = templateEvaluator.EvaluateStepScopeOutputs(scope.Outputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Info($"Caught exception from finalize scope '{scope.Name}'");
|
|
||||||
Trace.Error(ex);
|
|
||||||
executionContext.Error(ex);
|
|
||||||
executionContext.Complete(TaskResult.Failed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputs?.Count > 0)
|
|
||||||
{
|
|
||||||
var parentScopeName = scope.ParentName;
|
|
||||||
var contextName = scope.ContextName;
|
|
||||||
foreach (var pair in outputs)
|
|
||||||
{
|
|
||||||
var outputName = pair.Key;
|
|
||||||
var outputValue = pair.Value.ToString();
|
|
||||||
stepsContext.SetOutput(parentScopeName, contextName, outputName, outputValue, out var reference);
|
|
||||||
executionContext.Debug($"{reference}='{outputValue}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
executionContext.Complete(result, resultCode: resultCode);
|
executionContext.Complete(result, resultCode: resultCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"name": "string",
|
"name": "string",
|
||||||
"description": "string",
|
"description": "string",
|
||||||
"inputs": "inputs",
|
"inputs": "inputs",
|
||||||
"runs": "runs"
|
"runs": "runs",
|
||||||
|
"outputs": "outputs"
|
||||||
},
|
},
|
||||||
"loose-key-type": "non-empty-string",
|
"loose-key-type": "non-empty-string",
|
||||||
"loose-value-type": "any"
|
"loose-value-type": "any"
|
||||||
@@ -28,11 +29,26 @@
|
|||||||
"loose-value-type": "any"
|
"loose-value-type": "any"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"outputs": {
|
||||||
|
"mapping": {
|
||||||
|
"loose-key-type": "non-empty-string",
|
||||||
|
"loose-value-type": "outputs-attributes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs-attributes": {
|
||||||
|
"mapping": {
|
||||||
|
"properties": {
|
||||||
|
"description": "string",
|
||||||
|
"value": "output-value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"runs": {
|
"runs": {
|
||||||
"one-of": [
|
"one-of": [
|
||||||
"container-runs",
|
"container-runs",
|
||||||
"node12-runs",
|
"node12-runs",
|
||||||
"plugin-runs"
|
"plugin-runs",
|
||||||
|
"composite-runs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"container-runs": {
|
"container-runs": {
|
||||||
@@ -83,12 +99,49 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"composite-runs": {
|
||||||
|
"mapping": {
|
||||||
|
"properties": {
|
||||||
|
"using": "non-empty-string",
|
||||||
|
"steps": "composite-steps"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"composite-steps": {
|
||||||
|
"context": [
|
||||||
|
"github",
|
||||||
|
"strategy",
|
||||||
|
"matrix",
|
||||||
|
"steps",
|
||||||
|
"inputs",
|
||||||
|
"job",
|
||||||
|
"runner",
|
||||||
|
"env",
|
||||||
|
"hashFiles(1,255)"
|
||||||
|
],
|
||||||
|
"sequence": {
|
||||||
|
"item-type": "any"
|
||||||
|
}
|
||||||
|
},
|
||||||
"container-runs-context": {
|
"container-runs-context": {
|
||||||
"context": [
|
"context": [
|
||||||
"inputs"
|
"inputs"
|
||||||
],
|
],
|
||||||
"string": {}
|
"string": {}
|
||||||
},
|
},
|
||||||
|
"output-value": {
|
||||||
|
"context": [
|
||||||
|
"github",
|
||||||
|
"strategy",
|
||||||
|
"matrix",
|
||||||
|
"steps",
|
||||||
|
"inputs",
|
||||||
|
"job",
|
||||||
|
"runner",
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"string": {}
|
||||||
|
},
|
||||||
"input-default-context": {
|
"input-default-context": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
Environment = actionToClone.Environment?.Clone();
|
Environment = actionToClone.Environment?.Clone();
|
||||||
Inputs = actionToClone.Inputs?.Clone();
|
Inputs = actionToClone.Inputs?.Clone();
|
||||||
ContextName = actionToClone?.ContextName;
|
ContextName = actionToClone?.ContextName;
|
||||||
ScopeName = actionToClone?.ScopeName;
|
|
||||||
DisplayNameToken = actionToClone.DisplayNameToken?.Clone();
|
DisplayNameToken = actionToClone.DisplayNameToken?.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +40,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public TemplateToken DisplayNameToken { get; set; }
|
public TemplateToken DisplayNameToken { get; set; }
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public String ScopeName { get; set; }
|
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public String ContextName { get; set; }
|
public String ContextName { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
DictionaryContextData contextData,
|
DictionaryContextData contextData,
|
||||||
WorkspaceOptions workspaceOptions,
|
WorkspaceOptions workspaceOptions,
|
||||||
IEnumerable<JobStep> steps,
|
IEnumerable<JobStep> steps,
|
||||||
IEnumerable<ContextScope> scopes,
|
|
||||||
IList<String> fileTable,
|
IList<String> fileTable,
|
||||||
TemplateToken jobOutputs,
|
TemplateToken jobOutputs,
|
||||||
IList<TemplateToken> defaults)
|
IList<TemplateToken> defaults)
|
||||||
@@ -60,11 +59,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
m_maskHints = new List<MaskHint>(maskHints);
|
m_maskHints = new List<MaskHint>(maskHints);
|
||||||
m_steps = new List<JobStep>(steps);
|
m_steps = new List<JobStep>(steps);
|
||||||
|
|
||||||
if (scopes != null)
|
|
||||||
{
|
|
||||||
m_scopes = new List<ContextScope>(scopes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (environmentVariables?.Count > 0)
|
if (environmentVariables?.Count > 0)
|
||||||
{
|
{
|
||||||
m_environmentVariables = new List<TemplateToken>(environmentVariables);
|
m_environmentVariables = new List<TemplateToken>(environmentVariables);
|
||||||
@@ -261,18 +255,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<ContextScope> Scopes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_scopes == null)
|
|
||||||
{
|
|
||||||
m_scopes = new List<ContextScope>();
|
|
||||||
}
|
|
||||||
return m_scopes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the table of files used when parsing the pipeline (e.g. yaml files)
|
/// Gets the table of files used when parsing the pipeline (e.g. yaml files)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -415,11 +397,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
m_maskHints = new List<MaskHint>(this.m_maskHints.Distinct());
|
m_maskHints = new List<MaskHint>(this.m_maskHints.Distinct());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scopes?.Count == 0)
|
|
||||||
{
|
|
||||||
m_scopes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_variables?.Count == 0)
|
if (m_variables?.Count == 0)
|
||||||
{
|
{
|
||||||
m_variables = null;
|
m_variables = null;
|
||||||
@@ -447,9 +424,6 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
[DataMember(Name = "Steps", EmitDefaultValue = false)]
|
[DataMember(Name = "Steps", EmitDefaultValue = false)]
|
||||||
private List<JobStep> m_steps;
|
private List<JobStep> m_steps;
|
||||||
|
|
||||||
[DataMember(Name = "Scopes", EmitDefaultValue = false)]
|
|
||||||
private List<ContextScope> m_scopes;
|
|
||||||
|
|
||||||
[DataMember(Name = "Variables", EmitDefaultValue = false)]
|
[DataMember(Name = "Variables", EmitDefaultValue = false)]
|
||||||
private IDictionary<String, VariableValue> m_variables;
|
private IDictionary<String, VariableValue> m_variables;
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,12 @@ namespace GitHub.DistributedTask.Pipelines.ContextData
|
|||||||
var floored = Math.Floor(m_value);
|
var floored = Math.Floor(m_value);
|
||||||
if (m_value == floored && m_value <= (Double)Int32.MaxValue && m_value >= (Double)Int32.MinValue)
|
if (m_value == floored && m_value <= (Double)Int32.MaxValue && m_value >= (Double)Int32.MinValue)
|
||||||
{
|
{
|
||||||
Int32 flooredInt = (Int32)floored;
|
var flooredInt = (Int32)floored;
|
||||||
|
return (JToken)flooredInt;
|
||||||
|
}
|
||||||
|
else if (m_value == floored && m_value <= (Double)Int64.MaxValue && m_value >= (Double)Int64.MinValue)
|
||||||
|
{
|
||||||
|
var flooredInt = (Int64)floored;
|
||||||
return (JToken)flooredInt;
|
return (JToken)flooredInt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace GitHub.DistributedTask.Pipelines
|
|
||||||
{
|
|
||||||
[DataContract]
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public sealed class ContextScope
|
|
||||||
{
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public String Name { get; set; }
|
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public String ContextName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var index = Name.LastIndexOf('.');
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
return Name.Substring(index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[IgnoreDataMember]
|
|
||||||
public String ParentName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var index = Name.LastIndexOf('.');
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
return Name.Substring(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public TemplateToken Inputs { get; set; }
|
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
|
||||||
public TemplateToken Outputs { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String BooleanStrategyContext = "boolean-strategy-context";
|
public const String BooleanStrategyContext = "boolean-strategy-context";
|
||||||
public const String CancelTimeoutMinutes = "cancel-timeout-minutes";
|
public const String CancelTimeoutMinutes = "cancel-timeout-minutes";
|
||||||
public const String Cancelled = "cancelled";
|
public const String Cancelled = "cancelled";
|
||||||
public const String Checkout = "checkout";
|
public const String Clean= "clean";
|
||||||
public const String Clean = "clean";
|
|
||||||
public const String Container = "container";
|
public const String Container = "container";
|
||||||
public const String ContinueOnError = "continue-on-error";
|
public const String ContinueOnError = "continue-on-error";
|
||||||
public const String Defaults = "defaults";
|
public const String Defaults = "defaults";
|
||||||
@@ -23,7 +22,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String FailFast = "fail-fast";
|
public const String FailFast = "fail-fast";
|
||||||
public const String Failure = "failure";
|
public const String Failure = "failure";
|
||||||
public const String FetchDepth = "fetch-depth";
|
public const String FetchDepth = "fetch-depth";
|
||||||
public const String GeneratedId = "generated-id";
|
|
||||||
public const String GitHub = "github";
|
public const String GitHub = "github";
|
||||||
public const String HashFiles = "hashFiles";
|
public const String HashFiles = "hashFiles";
|
||||||
public const String Id = "id";
|
public const String Id = "id";
|
||||||
@@ -36,7 +34,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String JobIfResult = "job-if-result";
|
public const String JobIfResult = "job-if-result";
|
||||||
public const String JobOutputs = "job-outputs";
|
public const String JobOutputs = "job-outputs";
|
||||||
public const String Jobs = "jobs";
|
public const String Jobs = "jobs";
|
||||||
public const String Labels = "labels";
|
|
||||||
public const String Lfs = "lfs";
|
public const String Lfs = "lfs";
|
||||||
public const String Matrix = "matrix";
|
public const String Matrix = "matrix";
|
||||||
public const String MaxParallel = "max-parallel";
|
public const String MaxParallel = "max-parallel";
|
||||||
@@ -52,23 +49,18 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String Pool = "pool";
|
public const String Pool = "pool";
|
||||||
public const String Ports = "ports";
|
public const String Ports = "ports";
|
||||||
public const String Result = "result";
|
public const String Result = "result";
|
||||||
public const String RunDisplayPrefix = "Run ";
|
|
||||||
public const String Run = "run";
|
public const String Run = "run";
|
||||||
|
public const String RunDisplayPrefix = "Run ";
|
||||||
public const String Runner = "runner";
|
public const String Runner = "runner";
|
||||||
public const String RunsOn = "runs-on";
|
public const String RunsOn = "runs-on";
|
||||||
public const String Scope = "scope";
|
|
||||||
public const String Scopes = "scopes";
|
|
||||||
public const String Secrets = "secrets";
|
public const String Secrets = "secrets";
|
||||||
public const String Services = "services";
|
public const String Services = "services";
|
||||||
public const String Shell = "shell";
|
public const String Shell = "shell";
|
||||||
public const String Skipped = "skipped";
|
public const String Skipped = "skipped";
|
||||||
public const String StepEnv = "step-env";
|
public const String StepEnv = "step-env";
|
||||||
public const String StepIfResult = "step-if-result";
|
public const String StepIfResult = "step-if-result";
|
||||||
public const String Steps = "steps";
|
|
||||||
public const String StepsScopeInputs = "steps-scope-inputs";
|
|
||||||
public const String StepsScopeOutputs = "steps-scope-outputs";
|
|
||||||
public const String StepsTemplateRoot = "steps-template-root";
|
|
||||||
public const String StepWith = "step-with";
|
public const String StepWith = "step-with";
|
||||||
|
public const String Steps = "steps";
|
||||||
public const String Strategy = "strategy";
|
public const String Strategy = "strategy";
|
||||||
public const String StringStepsContext = "string-steps-context";
|
public const String StringStepsContext = "string-steps-context";
|
||||||
public const String StringStrategyContext = "string-strategy-context";
|
public const String StringStrategyContext = "string-strategy-context";
|
||||||
@@ -76,7 +68,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String Success = "success";
|
public const String Success = "success";
|
||||||
public const String Template = "template";
|
public const String Template = "template";
|
||||||
public const String TimeoutMinutes = "timeout-minutes";
|
public const String TimeoutMinutes = "timeout-minutes";
|
||||||
public const String Token = "token";
|
|
||||||
public const String Uses = "uses";
|
public const String Uses = "uses";
|
||||||
public const String VmImage = "vmImage";
|
public const String VmImage = "vmImage";
|
||||||
public const String Volumes = "volumes";
|
public const String Volumes = "volumes";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using GitHub.DistributedTask.Expressions2;
|
using GitHub.DistributedTask.Expressions2;
|
||||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||||
@@ -14,8 +14,62 @@ using Newtonsoft.Json.Linq;
|
|||||||
|
|
||||||
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||||
{
|
{
|
||||||
internal static class PipelineTemplateConverter
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public static class PipelineTemplateConverter
|
||||||
{
|
{
|
||||||
|
public static List<Step> ConvertToSteps(
|
||||||
|
TemplateContext context,
|
||||||
|
TemplateToken steps)
|
||||||
|
{
|
||||||
|
var stepsSequence = steps.AssertSequence($"job {PipelineTemplateConstants.Steps}");
|
||||||
|
|
||||||
|
var result = new List<Step>();
|
||||||
|
var nameBuilder = new ReferenceNameBuilder();
|
||||||
|
foreach (var stepsItem in stepsSequence)
|
||||||
|
{
|
||||||
|
var step = ConvertToStep(context, stepsItem, nameBuilder);
|
||||||
|
if (step != null) // step = null means we are hitting error during step conversion, there should be an error in context.errors
|
||||||
|
{
|
||||||
|
if (step.Enabled)
|
||||||
|
{
|
||||||
|
result.Add(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate context name if empty
|
||||||
|
foreach (ActionStep step in result)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(step.ContextName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = default(string);
|
||||||
|
switch (step.Reference.Type)
|
||||||
|
{
|
||||||
|
case ActionSourceType.ContainerRegistry:
|
||||||
|
var containerReference = step.Reference as ContainerRegistryReference;
|
||||||
|
name = containerReference.Image;
|
||||||
|
break;
|
||||||
|
case ActionSourceType.Repository:
|
||||||
|
var repositoryReference = step.Reference as RepositoryPathReference;
|
||||||
|
name = !String.IsNullOrEmpty(repositoryReference.Name) ? repositoryReference.Name : PipelineConstants.SelfAlias;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
name = "run";
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBuilder.AppendSegment($"__{name}");
|
||||||
|
step.ContextName = nameBuilder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal static Boolean ConvertToIfResult(
|
internal static Boolean ConvertToIfResult(
|
||||||
TemplateContext context,
|
TemplateContext context,
|
||||||
TemplateToken ifResult)
|
TemplateToken ifResult)
|
||||||
@@ -264,5 +318,311 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ActionStep ConvertToStep(
|
||||||
|
TemplateContext context,
|
||||||
|
TemplateToken stepsItem,
|
||||||
|
ReferenceNameBuilder nameBuilder)
|
||||||
|
{
|
||||||
|
var step = stepsItem.AssertMapping($"{PipelineTemplateConstants.Steps} item");
|
||||||
|
var continueOnError = default(ScalarToken);
|
||||||
|
var env = default(TemplateToken);
|
||||||
|
var id = default(StringToken);
|
||||||
|
var ifCondition = default(String);
|
||||||
|
var ifToken = default(ScalarToken);
|
||||||
|
var name = default(ScalarToken);
|
||||||
|
var run = default(ScalarToken);
|
||||||
|
var timeoutMinutes = default(ScalarToken);
|
||||||
|
var uses = default(StringToken);
|
||||||
|
var with = default(TemplateToken);
|
||||||
|
var workingDir = default(ScalarToken);
|
||||||
|
var path = default(ScalarToken);
|
||||||
|
var clean = default(ScalarToken);
|
||||||
|
var fetchDepth = default(ScalarToken);
|
||||||
|
var lfs = default(ScalarToken);
|
||||||
|
var submodules = default(ScalarToken);
|
||||||
|
var shell = default(ScalarToken);
|
||||||
|
|
||||||
|
foreach (var stepProperty in step)
|
||||||
|
{
|
||||||
|
var propertyName = stepProperty.Key.AssertString($"{PipelineTemplateConstants.Steps} item key");
|
||||||
|
|
||||||
|
switch (propertyName.Value)
|
||||||
|
{
|
||||||
|
case PipelineTemplateConstants.Clean:
|
||||||
|
clean = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Clean}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.ContinueOnError:
|
||||||
|
ConvertToStepContinueOnError(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
||||||
|
continueOnError = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} {PipelineTemplateConstants.ContinueOnError}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Env:
|
||||||
|
ConvertToStepEnvironment(context, stepProperty.Value, StringComparer.Ordinal, allowExpressions: true); // Validate early if possible
|
||||||
|
env = stepProperty.Value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.FetchDepth:
|
||||||
|
fetchDepth = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.FetchDepth}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Id:
|
||||||
|
id = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Id}");
|
||||||
|
if (!String.IsNullOrEmpty(id.Value))
|
||||||
|
{
|
||||||
|
if (!nameBuilder.TryAddKnownName(id.Value, out var error))
|
||||||
|
{
|
||||||
|
context.Error(id, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.If:
|
||||||
|
ifToken = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.If}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Lfs:
|
||||||
|
lfs = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Lfs}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Name:
|
||||||
|
name = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Name}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Path:
|
||||||
|
path = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Path}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Run:
|
||||||
|
run = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Run}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Shell:
|
||||||
|
shell = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Shell}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Submodules:
|
||||||
|
submodules = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Submodules}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.TimeoutMinutes:
|
||||||
|
ConvertToStepTimeout(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
||||||
|
timeoutMinutes = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.TimeoutMinutes}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.Uses:
|
||||||
|
uses = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.With:
|
||||||
|
ConvertToStepInputs(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
||||||
|
with = stepProperty.Value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PipelineTemplateConstants.WorkingDirectory:
|
||||||
|
workingDir = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.WorkingDirectory}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Steps} item key"); // throws
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixup the if-condition
|
||||||
|
ifCondition = ConvertToIfCondition(context, ifToken, false);
|
||||||
|
|
||||||
|
if (run != null)
|
||||||
|
{
|
||||||
|
var result = new ActionStep
|
||||||
|
{
|
||||||
|
ContextName = id?.Value,
|
||||||
|
ContinueOnError = continueOnError,
|
||||||
|
DisplayNameToken = name,
|
||||||
|
Condition = ifCondition,
|
||||||
|
TimeoutInMinutes = timeoutMinutes,
|
||||||
|
Environment = env,
|
||||||
|
Reference = new ScriptReference(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputs = new MappingToken(null, null, null);
|
||||||
|
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Script), run);
|
||||||
|
|
||||||
|
if (workingDir != null)
|
||||||
|
{
|
||||||
|
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.WorkingDirectory), workingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shell != null)
|
||||||
|
{
|
||||||
|
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Shell), shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Inputs = inputs;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uses.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}");
|
||||||
|
var result = new ActionStep
|
||||||
|
{
|
||||||
|
ContextName = id?.Value,
|
||||||
|
ContinueOnError = continueOnError,
|
||||||
|
DisplayNameToken = name,
|
||||||
|
Condition = ifCondition,
|
||||||
|
TimeoutInMinutes = timeoutMinutes,
|
||||||
|
Inputs = with,
|
||||||
|
Environment = env,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (uses.Value.StartsWith("docker://", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var image = uses.Value.Substring("docker://".Length);
|
||||||
|
result.Reference = new ContainerRegistryReference { Image = image };
|
||||||
|
}
|
||||||
|
else if (uses.Value.StartsWith("./") || uses.Value.StartsWith(".\\"))
|
||||||
|
{
|
||||||
|
result.Reference = new RepositoryPathReference
|
||||||
|
{
|
||||||
|
RepositoryType = PipelineConstants.SelfAlias,
|
||||||
|
Path = uses.Value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var usesSegments = uses.Value.Split('@');
|
||||||
|
var pathSegments = usesSegments[0].Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var gitRef = usesSegments.Length == 2 ? usesSegments[1] : String.Empty;
|
||||||
|
|
||||||
|
if (usesSegments.Length != 2 ||
|
||||||
|
pathSegments.Length < 2 ||
|
||||||
|
String.IsNullOrEmpty(pathSegments[0]) ||
|
||||||
|
String.IsNullOrEmpty(pathSegments[1]) ||
|
||||||
|
String.IsNullOrEmpty(gitRef))
|
||||||
|
{
|
||||||
|
// todo: loc
|
||||||
|
context.Error(uses, $"Expected format {{org}}/{{repo}}[/path]@ref. Actual '{uses.Value}'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var repositoryName = $"{pathSegments[0]}/{pathSegments[1]}";
|
||||||
|
var directoryPath = pathSegments.Length > 2 ? String.Join("/", pathSegments.Skip(2)) : String.Empty;
|
||||||
|
|
||||||
|
result.Reference = new RepositoryPathReference
|
||||||
|
{
|
||||||
|
RepositoryType = RepositoryTypes.GitHub,
|
||||||
|
Name = repositoryName,
|
||||||
|
Ref = gitRef,
|
||||||
|
Path = directoryPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When empty, default to "success()".
|
||||||
|
/// When a status function is not referenced, format as "success() && <CONDITION>".
|
||||||
|
/// </summary>
|
||||||
|
private static String ConvertToIfCondition(
|
||||||
|
TemplateContext context,
|
||||||
|
TemplateToken token,
|
||||||
|
Boolean isJob)
|
||||||
|
{
|
||||||
|
String condition;
|
||||||
|
if (token is null)
|
||||||
|
{
|
||||||
|
condition = null;
|
||||||
|
}
|
||||||
|
else if (token is BasicExpressionToken expressionToken)
|
||||||
|
{
|
||||||
|
condition = expressionToken.Expression;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var stringToken = token.AssertString($"{(isJob ? "job" : "step")} {PipelineTemplateConstants.If}");
|
||||||
|
condition = stringToken.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.IsNullOrWhiteSpace(condition))
|
||||||
|
{
|
||||||
|
return $"{PipelineTemplateConstants.Success}()";
|
||||||
|
}
|
||||||
|
|
||||||
|
var expressionParser = new ExpressionParser();
|
||||||
|
var functions = default(IFunctionInfo[]);
|
||||||
|
var namedValues = default(INamedValueInfo[]);
|
||||||
|
if (isJob)
|
||||||
|
{
|
||||||
|
namedValues = s_jobIfNamedValues;
|
||||||
|
// TODO: refactor into seperate functions
|
||||||
|
// functions = PhaseCondition.FunctionInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
namedValues = s_stepNamedValues;
|
||||||
|
functions = s_stepConditionFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
var node = default(ExpressionNode);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
context.Error(token, ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == null)
|
||||||
|
{
|
||||||
|
return $"{PipelineTemplateConstants.Success}()";
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasStatusFunction = node.Traverse().Any(x =>
|
||||||
|
{
|
||||||
|
if (x is Function function)
|
||||||
|
{
|
||||||
|
return String.Equals(function.Name, PipelineTemplateConstants.Always, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
String.Equals(function.Name, PipelineTemplateConstants.Cancelled, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
String.Equals(function.Name, PipelineTemplateConstants.Failure, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
String.Equals(function.Name, PipelineTemplateConstants.Success, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasStatusFunction ? condition : $"{PipelineTemplateConstants.Success}() && ({condition})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly INamedValueInfo[] s_jobIfNamedValues = new INamedValueInfo[]
|
||||||
|
{
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
|
||||||
|
};
|
||||||
|
private static readonly INamedValueInfo[] s_stepNamedValues = new INamedValueInfo[]
|
||||||
|
{
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Strategy),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Matrix),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Steps),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Job),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
|
||||||
|
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
|
||||||
|
};
|
||||||
|
private static readonly IFunctionInfo[] s_stepConditionFunctions = new IFunctionInfo[]
|
||||||
|
{
|
||||||
|
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Always, 0, 0),
|
||||||
|
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Cancelled, 0, 0),
|
||||||
|
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Failure, 0, 0),
|
||||||
|
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Success, 0, 0),
|
||||||
|
new FunctionInfo<NoOperation>(PipelineTemplateConstants.HashFiles, 1, Byte.MaxValue),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,60 +51,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
|
|
||||||
public Int32 MaxResultSize { get; set; } = 10 * 1024 * 1024; // 10 mb
|
public Int32 MaxResultSize { get; set; } = 10 * 1024 * 1024; // 10 mb
|
||||||
|
|
||||||
public DictionaryContextData EvaluateStepScopeInputs(
|
|
||||||
TemplateToken token,
|
|
||||||
DictionaryContextData contextData,
|
|
||||||
IList<IFunctionInfo> expressionFunctions)
|
|
||||||
{
|
|
||||||
var result = default(DictionaryContextData);
|
|
||||||
|
|
||||||
if (token != null && token.Type != TokenType.Null)
|
|
||||||
{
|
|
||||||
var context = CreateContext(contextData, expressionFunctions);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeInputs, token, 0, null, omitHeader: true);
|
|
||||||
context.Errors.Check();
|
|
||||||
result = token.ToContextData().AssertDictionary("steps scope inputs");
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
||||||
{
|
|
||||||
context.Errors.Add(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Errors.Check();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result ?? new DictionaryContextData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DictionaryContextData EvaluateStepScopeOutputs(
|
|
||||||
TemplateToken token,
|
|
||||||
DictionaryContextData contextData,
|
|
||||||
IList<IFunctionInfo> expressionFunctions)
|
|
||||||
{
|
|
||||||
var result = default(DictionaryContextData);
|
|
||||||
|
|
||||||
if (token != null && token.Type != TokenType.Null)
|
|
||||||
{
|
|
||||||
var context = CreateContext(contextData, expressionFunctions);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsScopeOutputs, token, 0, null, omitHeader: true);
|
|
||||||
context.Errors.Check();
|
|
||||||
result = token.ToContextData().AssertDictionary("steps scope outputs");
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
||||||
{
|
|
||||||
context.Errors.Add(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Errors.Check();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result ?? new DictionaryContextData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean EvaluateStepContinueOnError(
|
public Boolean EvaluateStepContinueOnError(
|
||||||
TemplateToken token,
|
TemplateToken token,
|
||||||
DictionaryContextData contextData,
|
DictionaryContextData contextData,
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text;
|
||||||
|
using GitHub.DistributedTask.Pipelines.Validation;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||||
|
{
|
||||||
|
internal sealed class ReferenceNameBuilder
|
||||||
|
{
|
||||||
|
internal void AppendSegment(String value)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_name.Length == 0)
|
||||||
|
{
|
||||||
|
var first = value[0];
|
||||||
|
if ((first >= 'a' && first <= 'z') ||
|
||||||
|
(first >= 'A' && first <= 'Z') ||
|
||||||
|
first == '_')
|
||||||
|
{
|
||||||
|
// Legal first char
|
||||||
|
}
|
||||||
|
else if ((first >= '0' && first <= '9') || first == '-')
|
||||||
|
{
|
||||||
|
// Illegal first char, but legal char.
|
||||||
|
// Prepend "_".
|
||||||
|
m_name.Append("_");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Illegal char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Separator
|
||||||
|
m_name.Append(c_separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var c in value)
|
||||||
|
{
|
||||||
|
if ((c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') ||
|
||||||
|
c == '_' ||
|
||||||
|
c == '-')
|
||||||
|
{
|
||||||
|
// Legal
|
||||||
|
m_name.Append(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Illegal
|
||||||
|
m_name.Append("_");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String Build()
|
||||||
|
{
|
||||||
|
var original = m_name.Length > 0 ? m_name.ToString() : "job";
|
||||||
|
|
||||||
|
var attempt = 1;
|
||||||
|
var suffix = default(String);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (attempt == 1)
|
||||||
|
{
|
||||||
|
suffix = String.Empty;
|
||||||
|
}
|
||||||
|
else if (attempt < 1000)
|
||||||
|
{
|
||||||
|
suffix = String.Format(CultureInfo.InvariantCulture, "_{0}", attempt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unable to create a unique name");
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidate = original.Substring(0, Math.Min(original.Length, PipelineConstants.MaxNodeNameLength - suffix.Length)) + suffix;
|
||||||
|
|
||||||
|
if (m_distinctNames.Add(candidate))
|
||||||
|
{
|
||||||
|
m_name.Clear();
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Boolean TryAddKnownName(
|
||||||
|
String value,
|
||||||
|
out String error)
|
||||||
|
{
|
||||||
|
if (!NameValidation.IsValid(value, allowHyphens: true) && value.Length < PipelineConstants.MaxNodeNameLength)
|
||||||
|
{
|
||||||
|
error = $"The identifier '{value}' is invalid. IDs may only contain alphanumeric characters, '_', and '-'. IDs must start with a letter or '_' and and must be less than {PipelineConstants.MaxNodeNameLength} characters.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!m_distinctNames.Add(value))
|
||||||
|
{
|
||||||
|
error = $"The identifier '{value}' may not be used more than once within the same scope.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const String c_separator = "_";
|
||||||
|
private readonly HashSet<String> m_distinctNames = new HashSet<String>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly StringBuilder m_name = new StringBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,5 +94,12 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
public static readonly String Resources = "resources";
|
public static readonly String Resources = "resources";
|
||||||
public static readonly String All = "all";
|
public static readonly String All = "all";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ScriptStepInputs
|
||||||
|
{
|
||||||
|
public static readonly String Script = "script";
|
||||||
|
public static readonly String WorkingDirectory = "workingDirectory";
|
||||||
|
public static readonly String Shell = "shell";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,116 +16,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"steps-template-root": {
|
|
||||||
"description": "Steps template file",
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"inputs": "steps-template-inputs",
|
|
||||||
"outputs": "steps-template-outputs",
|
|
||||||
"steps": "steps-in-template"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-scope-inputs": {
|
|
||||||
"description": "Used when evaluating steps scope inputs",
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "steps-scope-input-value"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-scope-input-value": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"one-of": [
|
|
||||||
"string",
|
|
||||||
"sequence",
|
|
||||||
"mapping"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-scope-outputs": {
|
|
||||||
"description": "Used when evaluating steps scope outputs",
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "steps-scope-output-value"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-scope-output-value": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"string": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-inputs": {
|
|
||||||
"description": "Allowed inputs in a steps template",
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "steps-template-input-value"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-input-value": {
|
|
||||||
"description": "Default input values for a steps template",
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix"
|
|
||||||
],
|
|
||||||
"one-of": [
|
|
||||||
"string",
|
|
||||||
"sequence",
|
|
||||||
"mapping"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-outputs": {
|
|
||||||
"description": "Output mapping for a steps template",
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "steps-template-output-value"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-output-value": {
|
|
||||||
"description": "Output values for a steps template",
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"string": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"workflow-defaults": {
|
"workflow-defaults": {
|
||||||
"mapping": {
|
"mapping": {
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -240,54 +130,27 @@
|
|||||||
"matrix": {
|
"matrix": {
|
||||||
"mapping": {
|
"mapping": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"include": "matrix-include",
|
"include": "matrix-filter",
|
||||||
"exclude": "matrix-exclude"
|
"exclude": "matrix-filter"
|
||||||
},
|
},
|
||||||
"loose-key-type": "non-empty-string",
|
"loose-key-type": "non-empty-string",
|
||||||
"loose-value-type": "sequence"
|
"loose-value-type": "sequence"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"matrix-include": {
|
"matrix-filter": {
|
||||||
"sequence": {
|
"sequence": {
|
||||||
"item-type": "matrix-include-item"
|
"item-type": "matrix-filter-item"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"matrix-include-item": {
|
"matrix-filter-item": {
|
||||||
"mapping": {
|
"mapping": {
|
||||||
"loose-key-type": "non-empty-string",
|
"loose-key-type": "non-empty-string",
|
||||||
"loose-value-type": "any"
|
"loose-value-type": "any"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"matrix-exclude": {
|
|
||||||
"sequence": {
|
|
||||||
"item-type": "matrix-exclude-item"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"matrix-exclude-item": {
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "matrix-exclude-filter-item"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"matrix-exclude-filter-item": {
|
|
||||||
"one-of": [
|
|
||||||
"string",
|
|
||||||
"matrix-exclude-mapping-filter"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"matrix-exclude-mapping-filter": {
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "matrix-exclude-filter-item"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"runs-on": {
|
"runs-on": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -364,25 +227,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"steps-in-template": {
|
|
||||||
"sequence": {
|
|
||||||
"item-type": "steps-item-in-template"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-item": {
|
"steps-item": {
|
||||||
"one-of": [
|
"one-of": [
|
||||||
"run-step",
|
"run-step",
|
||||||
"regular-step",
|
"regular-step"
|
||||||
"steps-template-reference"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-item-in-template": {
|
|
||||||
"one-of": [
|
|
||||||
"run-step-in-template",
|
|
||||||
"regular-step-in-template",
|
|
||||||
"steps-template-reference-in-template"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -405,25 +253,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"run-step-in-template": {
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"name": "string-steps-context-in-template",
|
|
||||||
"id": "non-empty-string",
|
|
||||||
"if": "step-if-in-template",
|
|
||||||
"timeout-minutes": "number-steps-context-in-template",
|
|
||||||
"run": {
|
|
||||||
"type": "string-steps-context-in-template",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"continue-on-error": "boolean-steps-context-in-template",
|
|
||||||
"env": "step-env-in-template",
|
|
||||||
"working-directory": "string-steps-context-in-template",
|
|
||||||
"shell": "non-empty-string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"regular-step": {
|
"regular-step": {
|
||||||
"mapping": {
|
"mapping": {
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -442,24 +271,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"regular-step-in-template": {
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"name": "string-steps-context-in-template",
|
|
||||||
"id": "non-empty-string",
|
|
||||||
"if": "step-if-in-template",
|
|
||||||
"continue-on-error": "boolean-steps-context-in-template",
|
|
||||||
"timeout-minutes": "number-steps-context-in-template",
|
|
||||||
"uses": {
|
|
||||||
"type": "non-empty-string",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"with": "step-with-in-template",
|
|
||||||
"env": "step-env-in-template"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"step-if": {
|
"step-if": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -479,26 +290,6 @@
|
|||||||
"string": {}
|
"string": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
"step-if-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"always(0,0)",
|
|
||||||
"failure(0,0)",
|
|
||||||
"cancelled(0,0)",
|
|
||||||
"success(0,0)",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"string": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"step-if-result": {
|
"step-if-result": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -524,89 +315,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"step-if-result-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"always(0,0)",
|
|
||||||
"failure(0,0)",
|
|
||||||
"cancelled(0,0)",
|
|
||||||
"success(0,0)",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"one-of": [
|
|
||||||
"null",
|
|
||||||
"boolean",
|
|
||||||
"number",
|
|
||||||
"string",
|
|
||||||
"sequence",
|
|
||||||
"mapping"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-reference": {
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"template": "non-empty-string",
|
|
||||||
"id": "non-empty-string",
|
|
||||||
"inputs": "steps-template-reference-inputs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-reference-in-template": {
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"template": "non-empty-string",
|
|
||||||
"id": "non-empty-string",
|
|
||||||
"inputs": "steps-template-reference-inputs-in-template"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-reference-inputs": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"steps-template-reference-inputs-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env"
|
|
||||||
],
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"step-env": {
|
"step-env": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -626,26 +334,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"step-env-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"step-with": {
|
"step-with": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -665,26 +353,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"step-with-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"mapping": {
|
|
||||||
"loose-key-type": "non-empty-string",
|
|
||||||
"loose-value-type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"container": {
|
"container": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -801,23 +469,6 @@
|
|||||||
"boolean": {}
|
"boolean": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
"boolean-steps-context-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"boolean": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"number-steps-context": {
|
"number-steps-context": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -834,23 +485,6 @@
|
|||||||
"number": {}
|
"number": {}
|
||||||
},
|
},
|
||||||
|
|
||||||
"number-steps-context-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"number": {}
|
|
||||||
},
|
|
||||||
|
|
||||||
"string-runner-context": {
|
"string-runner-context": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
@@ -880,23 +514,6 @@
|
|||||||
"hashFiles(1,255)"
|
"hashFiles(1,255)"
|
||||||
],
|
],
|
||||||
"string": {}
|
"string": {}
|
||||||
},
|
|
||||||
|
|
||||||
"string-steps-context-in-template": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"string": {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
this.OSDescription = referenceToBeCloned.OSDescription;
|
this.OSDescription = referenceToBeCloned.OSDescription;
|
||||||
this.ProvisioningState = referenceToBeCloned.ProvisioningState;
|
this.ProvisioningState = referenceToBeCloned.ProvisioningState;
|
||||||
this.AccessPoint = referenceToBeCloned.AccessPoint;
|
this.AccessPoint = referenceToBeCloned.AccessPoint;
|
||||||
|
this.Ephemeral = referenceToBeCloned.Ephemeral;
|
||||||
|
|
||||||
if (referenceToBeCloned.m_links != null)
|
if (referenceToBeCloned.m_links != null)
|
||||||
{
|
{
|
||||||
@@ -81,6 +82,16 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signifies that this Agent can only run one job and will be removed by the server after that one job finish.
|
||||||
|
/// </summary>
|
||||||
|
[DataMember]
|
||||||
|
public bool? Ephemeral
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not the agent is online.
|
/// Whether or not the agent is online.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
using Xunit;
|
// using Xunit;
|
||||||
using System.IO;
|
// using System.IO;
|
||||||
using System.Net.Http;
|
// using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
// using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common.Tests
|
// namespace GitHub.Runner.Common.Tests
|
||||||
{
|
// {
|
||||||
public sealed class DotnetsdkDownloadScriptL0
|
// public sealed class DotnetsdkDownloadScriptL0
|
||||||
{
|
// {
|
||||||
[Fact]
|
// [Fact]
|
||||||
[Trait("Level", "L0")]
|
// [Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
// [Trait("Category", "Runner")]
|
||||||
public async Task EnsureDotnetsdkBashDownloadScriptUpToDate()
|
// public async Task EnsureDotnetsdkBashDownloadScriptUpToDate()
|
||||||
{
|
// {
|
||||||
string shDownloadUrl = "https://dot.net/v1/dotnet-install.sh";
|
// string shDownloadUrl = "https://dot.net/v1/dotnet-install.sh";
|
||||||
|
|
||||||
using (HttpClient downloadClient = new HttpClient())
|
// using (HttpClient downloadClient = new HttpClient())
|
||||||
{
|
// {
|
||||||
var response = await downloadClient.GetAsync("https://www.bing.com");
|
// var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||||
if (!response.IsSuccessStatusCode)
|
// if (!response.IsSuccessStatusCode)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
string shScript = await downloadClient.GetStringAsync(shDownloadUrl);
|
// string shScript = await downloadClient.GetStringAsync(shDownloadUrl);
|
||||||
|
|
||||||
string existingShScript = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.sh"));
|
// string existingShScript = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.sh"));
|
||||||
|
|
||||||
bool shScriptMatched = string.Equals(shScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingShScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
// bool shScriptMatched = string.Equals(shScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingShScript.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||||
Assert.True(shScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.sh with content from https://dot.net/v1/dotnet-install.sh");
|
// Assert.True(shScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.sh with content from https://dot.net/v1/dotnet-install.sh");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
[Fact]
|
// [Fact]
|
||||||
[Trait("Level", "L0")]
|
// [Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
// [Trait("Category", "Runner")]
|
||||||
public async Task EnsureDotnetsdkPowershellDownloadScriptUpToDate()
|
// public async Task EnsureDotnetsdkPowershellDownloadScriptUpToDate()
|
||||||
{
|
// {
|
||||||
string ps1DownloadUrl = "https://dot.net/v1/dotnet-install.ps1";
|
// string ps1DownloadUrl = "https://dot.net/v1/dotnet-install.ps1";
|
||||||
|
|
||||||
using (HttpClient downloadClient = new HttpClient())
|
// using (HttpClient downloadClient = new HttpClient())
|
||||||
{
|
// {
|
||||||
var response = await downloadClient.GetAsync("https://www.bing.com");
|
// var response = await downloadClient.GetAsync("https://www.bing.com");
|
||||||
if (!response.IsSuccessStatusCode)
|
// if (!response.IsSuccessStatusCode)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
string ps1Script = await downloadClient.GetStringAsync(ps1DownloadUrl);
|
// string ps1Script = await downloadClient.GetStringAsync(ps1DownloadUrl);
|
||||||
|
|
||||||
string existingPs1Script = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.ps1"));
|
// string existingPs1Script = File.ReadAllText(Path.Combine(TestUtil.GetSrcPath(), "Misc/dotnet-install.ps1"));
|
||||||
|
|
||||||
bool ps1ScriptMatched = string.Equals(ps1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingPs1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
// bool ps1ScriptMatched = string.Equals(ps1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"), existingPs1Script.TrimEnd('\n', '\r', '\0').Replace("\r\n", "\n").Replace("\r", "\n"));
|
||||||
Assert.True(ps1ScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.ps1 with content from https://dot.net/v1/dotnet-install.ps1");
|
// Assert.True(ps1ScriptMatched, "Fix the test by updating Src/Misc/dotnet-install.ps1 with content from https://dot.net/v1/dotnet-install.ps1");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||||
TimelineReference timeline = null;
|
TimelineReference timeline = null;
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||||
TimelineReference timeline = null;
|
TimelineReference timeline = null;
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
return new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JobCancelMessage CreateJobCancelMessage()
|
private JobCancelMessage CreateJobCancelMessage()
|
||||||
@@ -243,7 +243,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
runner.Initialize(hc);
|
runner.Initialize(hc);
|
||||||
var settings = new RunnerSettings
|
var settings = new RunnerSettings
|
||||||
{
|
{
|
||||||
PoolId = 43242
|
PoolId = 43242,
|
||||||
|
Ephemeral = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var message = new TaskAgentMessage()
|
var message = new TaskAgentMessage()
|
||||||
@@ -294,7 +295,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
//Act
|
//Act
|
||||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
var command = new CommandSettings(hc, new string[] { "run" });
|
||||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
@@ -332,7 +333,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
runner.Initialize(hc);
|
runner.Initialize(hc);
|
||||||
var settings = new RunnerSettings
|
var settings = new RunnerSettings
|
||||||
{
|
{
|
||||||
PoolId = 43242
|
PoolId = 43242,
|
||||||
|
Ephemeral = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var message1 = new TaskAgentMessage()
|
var message1 = new TaskAgentMessage()
|
||||||
@@ -390,7 +392,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
//Act
|
//Act
|
||||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
var command = new CommandSettings(hc, new string[] { "run" });
|
||||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
@@ -431,7 +433,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
var settings = new RunnerSettings
|
var settings = new RunnerSettings
|
||||||
{
|
{
|
||||||
PoolId = 43242,
|
PoolId = 43242,
|
||||||
AgentId = 5678
|
AgentId = 5678,
|
||||||
|
Ephemeral = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var message1 = new TaskAgentMessage()
|
var message1 = new TaskAgentMessage()
|
||||||
@@ -475,7 +478,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
|
||||||
//Act
|
//Act
|
||||||
var command = new CommandSettings(hc, new string[] { "run", "--once" });
|
var command = new CommandSettings(hc, new string[] { "run" });
|
||||||
Task<int> runnerTask = runner.ExecuteCommand(command);
|
Task<int> runnerTask = runner.ExecuteCommand(command);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||||
});
|
});
|
||||||
|
|
||||||
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
|
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
||||||
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken", null));
|
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "##[stop-commands]stopToken", null));
|
||||||
Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
Assert.False(_commandManager.TryProcessCommand(_ec.Object, "##[set-env name=foo]bar", null));
|
||||||
@@ -119,8 +119,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
_ec.SetupAllProperties();
|
|
||||||
|
|
||||||
Assert.False(_ec.Object.EchoOnActionCommand);
|
Assert.False(_ec.Object.EchoOnActionCommand);
|
||||||
|
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::on", null));
|
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::on", null));
|
||||||
@@ -150,7 +148,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -204,8 +202,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
_ec.SetupAllProperties();
|
|
||||||
|
|
||||||
// Echo commands below are considered "processed", but are invalid
|
// Echo commands below are considered "processed", but are invalid
|
||||||
// 1. Invalid echo value
|
// 1. Invalid echo value
|
||||||
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::invalid", null));
|
Assert.True(_commandManager.TryProcessCommand(_ec.Object, "::echo::invalid", null));
|
||||||
@@ -287,6 +283,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
// Execution context
|
// Execution context
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
|
_ec.SetupAllProperties();
|
||||||
|
_ec.Setup(x => x.Global).Returns(new GlobalContext());
|
||||||
|
|
||||||
// Command manager
|
// Command manager
|
||||||
_commandManager = new ActionCommandManager();
|
_commandManager = new ActionCommandManager();
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = ActionName,
|
Name = ActionName,
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = "GitHub"
|
RepositoryType = "GitHub"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
// Return a valid action from GHES via mock
|
// Return a valid action from GHES via mock
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
const string ApiUrl = "https://ghes.example.com/api/v3";
|
||||||
string expectedArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
string expectedArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "main");
|
||||||
string archiveFile = await CreateRepoArchive();
|
string archiveFile = await CreateRepoArchive();
|
||||||
using var stream = File.OpenRead(archiveFile);
|
using var stream = File.OpenRead(archiveFile);
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
var mockClientHandler = new Mock<HttpClientHandler>();
|
||||||
@@ -159,10 +159,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main.completed");
|
||||||
Assert.True(File.Exists(watermarkFile));
|
Assert.True(File.Exists(watermarkFile));
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
|
||||||
Assert.True(File.Exists(actionYamlFile));
|
Assert.True(File.Exists(actionYamlFile));
|
||||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = ActionName,
|
Name = ActionName,
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = "GitHub"
|
RepositoryType = "GitHub"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +199,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
// Return a valid action from GHES via mock
|
// Return a valid action from GHES via mock
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
const string ApiUrl = "https://ghes.example.com/api/v3";
|
||||||
string builtInArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
string builtInArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "main");
|
||||||
string dotcomArchiveLink = GetLinkToActionArchive("https://api.github.com", ActionName, "master");
|
string dotcomArchiveLink = GetLinkToActionArchive("https://api.github.com", ActionName, "main");
|
||||||
string archiveFile = await CreateRepoArchive();
|
string archiveFile = await CreateRepoArchive();
|
||||||
using var stream = File.OpenRead(archiveFile);
|
using var stream = File.OpenRead(archiveFile);
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
var mockClientHandler = new Mock<HttpClientHandler>();
|
||||||
@@ -220,10 +220,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main.completed");
|
||||||
Assert.True(File.Exists(watermarkFile));
|
Assert.True(File.Exists(watermarkFile));
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
|
||||||
Assert.True(File.Exists(actionYamlFile));
|
Assert.True(File.Exists(actionYamlFile));
|
||||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
||||||
}
|
}
|
||||||
@@ -252,7 +252,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = ActionName,
|
Name = ActionName,
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = "GitHub"
|
RepositoryType = "GitHub"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +260,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
// Return a valid action from GHES via mock
|
// Return a valid action from GHES via mock
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
const string ApiUrl = "https://ghes.example.com/api/v3";
|
||||||
string archiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
string archiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "main");
|
||||||
string archiveFile = await CreateRepoArchive();
|
string archiveFile = await CreateRepoArchive();
|
||||||
using var stream = File.OpenRead(archiveFile);
|
using var stream = File.OpenRead(archiveFile);
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
var mockClientHandler = new Mock<HttpClientHandler>();
|
||||||
@@ -280,10 +280,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
//Assert
|
//Assert
|
||||||
await Assert.ThrowsAsync<ActionNotFoundException>(action);
|
await Assert.ThrowsAsync<ActionNotFoundException>(action);
|
||||||
|
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main.completed");
|
||||||
Assert.False(File.Exists(watermarkFile));
|
Assert.False(File.Exists(watermarkFile));
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
|
||||||
Assert.False(File.Exists(actionYamlFile));
|
Assert.False(File.Exists(actionYamlFile));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -846,7 +846,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
var traceFile = Path.GetTempFileName();
|
var traceFile = Path.GetTempFileName();
|
||||||
File.Copy(_hc.TraceFileName, traceFile, true);
|
File.Copy(_hc.TraceFileName, traceFile, true);
|
||||||
Assert.Contains("Entry javascript file is not provided.", File.ReadAllText(traceFile));
|
Assert.Contains("You are using a JavaScript Action but there is not an entry JavaScript file provided in", File.ReadAllText(traceFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -1278,7 +1278,7 @@ runs:
|
|||||||
";
|
";
|
||||||
Pipelines.ActionStep instance;
|
Pipelines.ActionStep instance;
|
||||||
string directory;
|
string directory;
|
||||||
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "master");
|
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "main");
|
||||||
string file = Path.Combine(directory, Constants.Path.ActionManifestYamlFile);
|
string file = Path.Combine(directory, Constants.Path.ActionManifestYamlFile);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
File.WriteAllText(file, Content);
|
File.WriteAllText(file, Content);
|
||||||
@@ -1288,7 +1288,7 @@ runs:
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = "GitHub/actions",
|
Name = "GitHub/actions",
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2466,7 +2466,7 @@ runs:
|
|||||||
{
|
{
|
||||||
var traceFile = Path.GetTempFileName();
|
var traceFile = Path.GetTempFileName();
|
||||||
File.Copy(_hc.TraceFileName, traceFile, true);
|
File.Copy(_hc.TraceFileName, traceFile, true);
|
||||||
Assert.Contains("Entry javascript file is not provided.", File.ReadAllText(traceFile));
|
Assert.Contains("You are using a JavaScript Action but there is not an entry JavaScript file provided in", File.ReadAllText(traceFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -2898,7 +2898,7 @@ runs:
|
|||||||
";
|
";
|
||||||
Pipelines.ActionStep instance;
|
Pipelines.ActionStep instance;
|
||||||
string directory;
|
string directory;
|
||||||
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "master");
|
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "main");
|
||||||
string file = Path.Combine(directory, Constants.Path.ActionManifestYamlFile);
|
string file = Path.Combine(directory, Constants.Path.ActionManifestYamlFile);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
File.WriteAllText(file, Content);
|
File.WriteAllText(file, Content);
|
||||||
@@ -2908,7 +2908,7 @@ runs:
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = "GitHub/actions",
|
Name = "GitHub/actions",
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3453,7 +3453,7 @@ runs:
|
|||||||
|
|
||||||
private void CreateAction(string yamlContent, out Pipelines.ActionStep instance, out string directory)
|
private void CreateAction(string yamlContent, out Pipelines.ActionStep instance, out string directory)
|
||||||
{
|
{
|
||||||
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "master");
|
directory = Path.Combine(_workFolder, Constants.Path.ActionsDirectory, "GitHub/actions".Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), "main");
|
||||||
string file = Path.Combine(directory, Constants.Path.ActionManifestYmlFile);
|
string file = Path.Combine(directory, Constants.Path.ActionManifestYmlFile);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||||
File.WriteAllText(file, yamlContent);
|
File.WriteAllText(file, yamlContent);
|
||||||
@@ -3463,7 +3463,7 @@ runs:
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = "GitHub/actions",
|
Name = "GitHub/actions",
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
RepositoryType = Pipelines.RepositoryTypes.GitHub
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3481,7 +3481,7 @@ runs:
|
|||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = "GitHub/actions",
|
Name = "GitHub/actions",
|
||||||
Ref = "master",
|
Ref = "main",
|
||||||
RepositoryType = Pipelines.PipelineConstants.SelfAlias
|
RepositoryType = Pipelines.PipelineConstants.SelfAlias
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3575,16 +3575,18 @@ runs:
|
|||||||
_workFolder = _hc.GetDirectory(WellKnownDirectory.Work);
|
_workFolder = _hc.GetDirectory(WellKnownDirectory.Work);
|
||||||
|
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
|
_ec.Setup(x => x.Global).Returns(new GlobalContext());
|
||||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||||
var variables = new Dictionary<string, VariableValue>();
|
var variables = new Dictionary<string, VariableValue>();
|
||||||
if (newActionMetadata)
|
if (newActionMetadata)
|
||||||
{
|
{
|
||||||
variables["DistributedTask.NewActionMetadata"] = "true";
|
variables["DistributedTask.NewActionMetadata"] = "true";
|
||||||
}
|
}
|
||||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, variables));
|
_ec.Object.Global.Variables = new Variables(_hc, variables);
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||||
_ec.Setup(x => x.Plan).Returns(new TaskOrchestrationPlanReference());
|
_ec.Object.Global.FileTable = new List<String>();
|
||||||
|
_ec.Object.Global.Plan = new TaskOrchestrationPlanReference();
|
||||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
|
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
|
||||||
|
|||||||
@@ -717,7 +717,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.ExpressionValues["github"] = new DictionaryContextData
|
_ec.Object.ExpressionValues["github"] = new DictionaryContextData
|
||||||
{
|
{
|
||||||
{ "ref", new StringContextData("refs/heads/master") },
|
{ "ref", new StringContextData("refs/heads/main") },
|
||||||
};
|
};
|
||||||
_ec.Object.ExpressionValues["strategy"] = new DictionaryContextData();
|
_ec.Object.ExpressionValues["strategy"] = new DictionaryContextData();
|
||||||
_ec.Object.ExpressionValues["matrix"] = new DictionaryContextData();
|
_ec.Object.ExpressionValues["matrix"] = new DictionaryContextData();
|
||||||
@@ -737,7 +737,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"));
|
result = actionManifest.EvaluateDefaultInput(_ec.Object, "testInput", new BasicExpressionToken(null, null, null, "github.ref"));
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
Assert.Equal("refs/heads/master", result);
|
Assert.Equal("refs/heads/main", result);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -754,9 +754,14 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
_hc = new TestHostContext(this, name);
|
_hc = new TestHostContext(this, name);
|
||||||
|
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
_ec.Setup(x => x.WriteDebug).Returns(true);
|
_ec.Setup(x => x.Global)
|
||||||
|
.Returns(new GlobalContext
|
||||||
|
{
|
||||||
|
FileTable = new List<String>(),
|
||||||
|
Variables = new Variables(_hc, new Dictionary<string, VariableValue>()),
|
||||||
|
WriteDebug = true,
|
||||||
|
});
|
||||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
|
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
||||||
|
|||||||
@@ -375,14 +375,16 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
|
_ec.Setup(x => x.Global).Returns(new GlobalContext());
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
||||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||||
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
||||||
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
|
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
||||||
|
_ec.Object.Global.FileTable = new List<String>();
|
||||||
_ec.Setup(x => x.SetGitHubContext(It.IsAny<string>(), It.IsAny<string>()));
|
_ec.Setup(x => x.SetGitHubContext(It.IsAny<string>(), It.IsAny<string>()));
|
||||||
_ec.Setup(x => x.GetGitHubContext(It.IsAny<string>())).Returns("{\"foo\":\"bar\"}");
|
_ec.Setup(x => x.GetGitHubContext(It.IsAny<string>())).Returns("{\"foo\":\"bar\"}");
|
||||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
|
_ec.Object.Global.Variables = new Variables(_hc, new Dictionary<string, VariableValue>());
|
||||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -102,7 +102,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -153,7 +153,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -251,7 +251,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -335,7 +335,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TimelineReference timeline = new TimelineReference();
|
TimelineReference timeline = new TimelineReference();
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
string jobName = "some job name";
|
string jobName = "some job name";
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||||
{
|
{
|
||||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||||
@@ -357,20 +357,20 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Act.
|
// Act.
|
||||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||||
|
|
||||||
jobContext.StepsContext.SetConclusion(null, "step1", ActionResult.Success);
|
jobContext.Global.StepsContext.SetConclusion(null, "step1", ActionResult.Success);
|
||||||
var conclusion1 = (jobContext.StepsContext.GetScope(null)["step1"] as DictionaryContextData)["conclusion"].ToString();
|
var conclusion1 = (jobContext.Global.StepsContext.GetScope(null)["step1"] as DictionaryContextData)["conclusion"].ToString();
|
||||||
Assert.Equal(conclusion1, conclusion1.ToLowerInvariant());
|
Assert.Equal(conclusion1, conclusion1.ToLowerInvariant());
|
||||||
|
|
||||||
jobContext.StepsContext.SetOutcome(null, "step2", ActionResult.Cancelled);
|
jobContext.Global.StepsContext.SetOutcome(null, "step2", ActionResult.Cancelled);
|
||||||
var outcome1 = (jobContext.StepsContext.GetScope(null)["step2"] as DictionaryContextData)["outcome"].ToString();
|
var outcome1 = (jobContext.Global.StepsContext.GetScope(null)["step2"] as DictionaryContextData)["outcome"].ToString();
|
||||||
Assert.Equal(outcome1, outcome1.ToLowerInvariant());
|
Assert.Equal(outcome1, outcome1.ToLowerInvariant());
|
||||||
|
|
||||||
jobContext.StepsContext.SetConclusion(null, "step3", ActionResult.Failure);
|
jobContext.Global.StepsContext.SetConclusion(null, "step3", ActionResult.Failure);
|
||||||
var conclusion2 = (jobContext.StepsContext.GetScope(null)["step3"] as DictionaryContextData)["conclusion"].ToString();
|
var conclusion2 = (jobContext.Global.StepsContext.GetScope(null)["step3"] as DictionaryContextData)["conclusion"].ToString();
|
||||||
Assert.Equal(conclusion2, conclusion2.ToLowerInvariant());
|
Assert.Equal(conclusion2, conclusion2.ToLowerInvariant());
|
||||||
|
|
||||||
jobContext.StepsContext.SetOutcome(null, "step4", ActionResult.Skipped);
|
jobContext.Global.StepsContext.SetOutcome(null, "step4", ActionResult.Skipped);
|
||||||
var outcome2 = (jobContext.StepsContext.GetScope(null)["step4"] as DictionaryContextData)["outcome"].ToString();
|
var outcome2 = (jobContext.Global.StepsContext.GetScope(null)["step4"] as DictionaryContextData)["outcome"].ToString();
|
||||||
Assert.Equal(outcome2, outcome2.ToLowerInvariant());
|
Assert.Equal(outcome2, outcome2.ToLowerInvariant());
|
||||||
|
|
||||||
jobContext.JobContext.Status = ActionResult.Success;
|
jobContext.JobContext.Status = ActionResult.Success;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
};
|
};
|
||||||
|
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null, null);
|
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null);
|
||||||
GitHubContext github = new GitHubContext();
|
GitHubContext github = new GitHubContext();
|
||||||
github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner");
|
github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner");
|
||||||
_message.ContextData.Add("github", github);
|
_message.ContextData.Add("github", github);
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
|
||||||
TimelineReference timeline = new Timeline(Guid.NewGuid());
|
TimelineReference timeline = new Timeline(Guid.NewGuid());
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null);
|
||||||
_message.Variables[Constants.Variables.System.Culture] = "en-US";
|
_message.Variables[Constants.Variables.System.Culture] = "en-US";
|
||||||
_message.Resources.Endpoints.Add(new ServiceEndpoint()
|
_message.Resources.Endpoints.Add(new ServiceEndpoint()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -955,12 +955,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
_variables = new Variables(hostContext, new Dictionary<string, DTWebApi.VariableValue>());
|
_variables = new Variables(hostContext, new Dictionary<string, DTWebApi.VariableValue>());
|
||||||
|
|
||||||
_executionContext = new Mock<IExecutionContext>();
|
_executionContext = new Mock<IExecutionContext>();
|
||||||
_executionContext.Setup(x => x.WriteDebug)
|
_executionContext.Setup(x => x.Global)
|
||||||
.Returns(true);
|
.Returns(new GlobalContext
|
||||||
_executionContext.Setup(x => x.Variables)
|
{
|
||||||
.Returns(_variables);
|
Container = jobContainer,
|
||||||
_executionContext.Setup(x => x.Container)
|
Variables = _variables,
|
||||||
.Returns(jobContainer);
|
WriteDebug = true,
|
||||||
|
});
|
||||||
_executionContext.Setup(x => x.GetMatchers())
|
_executionContext.Setup(x => x.GetMatchers())
|
||||||
.Returns(matchers?.Matchers ?? new List<IssueMatcherConfig>());
|
.Returns(matchers?.Matchers ?? new List<IssueMatcherConfig>());
|
||||||
_executionContext.Setup(x => x.Add(It.IsAny<OnMatcherChanged>()))
|
_executionContext.Setup(x => x.Add(It.IsAny<OnMatcherChanged>()))
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
// Setup the execution context.
|
// Setup the execution context.
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
|
_ec.Setup(x => x.Global).Returns(new GlobalContext());
|
||||||
|
|
||||||
GitHubContext githubContext = new GitHubContext();
|
GitHubContext githubContext = new GitHubContext();
|
||||||
_ec.Setup(x => x.GetGitHubContext("repository")).Returns("actions/runner");
|
_ec.Setup(x => x.GetGitHubContext("repository")).Returns("actions/runner");
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
};
|
};
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
_ec.SetupAllProperties();
|
_ec.SetupAllProperties();
|
||||||
_ec.Setup(x => x.Variables).Returns(_variables);
|
_ec.Setup(x => x.Global).Returns(new GlobalContext { WriteDebug = true });
|
||||||
|
_ec.Object.Global.Variables = _variables;
|
||||||
|
_ec.Object.Global.EnvironmentVariables = _env;
|
||||||
|
|
||||||
_contexts = new DictionaryContextData();
|
_contexts = new DictionaryContextData();
|
||||||
_jobContext = new JobContext();
|
_jobContext = new JobContext();
|
||||||
@@ -50,7 +52,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
_ec.Setup(x => x.JobContext).Returns(_jobContext);
|
_ec.Setup(x => x.JobContext).Returns(_jobContext);
|
||||||
|
|
||||||
_stepContext = new StepsContext();
|
_stepContext = new StepsContext();
|
||||||
_ec.Setup(x => x.StepsContext).Returns(_stepContext);
|
_ec.Object.Global.StepsContext = _stepContext;
|
||||||
|
|
||||||
_ec.Setup(x => x.PostJobSteps).Returns(new Stack<IStep>());
|
_ec.Setup(x => x.PostJobSteps).Returns(new Stack<IStep>());
|
||||||
|
|
||||||
@@ -80,7 +82,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -115,7 +117,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -154,7 +156,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -208,7 +210,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -287,7 +289,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -330,7 +332,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -361,7 +363,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -391,7 +393,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -417,7 +419,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -426,11 +428,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||||
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("100"));
|
Assert.Equal("100", step1.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("100"));
|
||||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("github_actions"));
|
Assert.Equal("github_actions", step1.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("github_actions"));
|
||||||
#else
|
#else
|
||||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("100"));
|
Assert.Equal("100", step1.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("100"));
|
||||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("github_actions"));
|
Assert.Equal("github_actions", step1.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("github_actions"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,7 +457,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -463,13 +465,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env3"].AssertString("github_actions"));
|
Assert.Equal("github_actions", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env3"].AssertString("github_actions"));
|
||||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertDictionary("env").ContainsKey("env2"));
|
Assert.False(step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env").ContainsKey("env2"));
|
||||||
#else
|
#else
|
||||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env3"].AssertString("github_actions"));
|
Assert.Equal("github_actions", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env3"].AssertString("github_actions"));
|
||||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env").ContainsKey("env2"));
|
Assert.False(step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env").ContainsKey("env2"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,7 +495,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -501,11 +503,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something"));
|
Assert.Equal("something", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something"));
|
||||||
#else
|
#else
|
||||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something"));
|
Assert.Equal("something", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,7 +526,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -560,7 +562,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -599,13 +601,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
// Setup the step execution context.
|
// Setup the step execution context.
|
||||||
var stepContext = new Mock<IExecutionContext>();
|
var stepContext = new Mock<IExecutionContext>();
|
||||||
stepContext.SetupAllProperties();
|
stepContext.SetupAllProperties();
|
||||||
stepContext.Setup(x => x.WriteDebug).Returns(true);
|
stepContext.Setup(x => x.Global).Returns(() => _ec.Object.Global);
|
||||||
stepContext.Setup(x => x.Variables).Returns(_variables);
|
stepContext.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||||
stepContext.Setup(x => x.EnvironmentVariables).Returns(_env);
|
|
||||||
stepContext.Setup(x => x.ExpressionValues).Returns(_contexts);
|
|
||||||
stepContext.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
stepContext.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||||
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
|
||||||
stepContext.Setup(x => x.ContextName).Returns(step.Object.Action.ContextName);
|
stepContext.Setup(x => x.ContextName).Returns(step.Object.Action.ContextName);
|
||||||
stepContext.Setup(x => x.Complete(It.IsAny<TaskResult?>(), It.IsAny<string>(), It.IsAny<string>()))
|
stepContext.Setup(x => x.Complete(It.IsAny<TaskResult?>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
.Callback((TaskResult? r, string currentOperation, string resultCode) =>
|
.Callback((TaskResult? r, string currentOperation, string resultCode) =>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
new Pipelines.ContextData.DictionaryContextData()
|
new Pipelines.ContextData.DictionaryContextData()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, new StringToken(null, null, null, "ubuntu"), sidecarContainers, null, variables, new List<MaskHint>(), resources, context, null, actions, null, null, null, null);
|
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, JobId, jobName, jobName, new StringToken(null, null, null, "ubuntu"), sidecarContainers, null, variables, new List<MaskHint>(), resources, context, null, actions, null, null, null);
|
||||||
return jobRequest;
|
return jobRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.267.0
|
2.299.0
|
||||||
|
|||||||
6
test_script.sh
Normal file
6
test_script.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apt-get update
|
||||||
|
apt-get install -y apt-transport-https gnupg2
|
||||||
|
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||||
|
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y kubectl
|
||||||
Reference in New Issue
Block a user