Compare commits

...

133 Commits

Author SHA1 Message Date
Tingluo Huang
f1ddeb0d06 Create 2.286.0 Runner release. 2021-12-21 10:51:03 -05:00
Tingluo Huang
9bfbc48f45 Prepare runner release 2.286.0. (#1574) 2021-12-21 10:50:14 -05:00
Tingluo Huang
ead1826afb Update codeql.yml 2021-12-21 10:32:55 -05:00
khaser
9de17f197c Deleted extra background in github-praph.png, which is displayed in README.md (#1432)
* github-praph.png deleted extra background

* background around tentacles of mascot also deleted
2021-12-21 10:29:18 -05:00
Hans Kratz
45decac397 Fix test failure: /bin/sleep on Macos 11 (Monterey) does not accept the suffix s. (#1472) 2021-12-21 10:27:48 -05:00
Edward Thomson
55ed60b9fc Direct people to Feedback or Support forums (#1571)
Many people open bug reports or feature requests in the `actions/runner`
repository that are more generally about GitHub Actions.  Often changes
in GitHub Actions are cross-cutting across multiple teams or feature
areas, so it's best if we direct people to the more general areas
(Actions Community Support or GitHub Feedback) so that we can get the
most eyes on the problem and give the quickest response.
2021-12-20 15:21:32 -05:00
George Karagoulis
698d3a2e66 Show service container logs on teardown (#1563)
* Update ContainerOperationProvider.cs

* Only print logs for service container jobs.
2021-12-20 10:55:47 -05:00
Tingluo Huang
d0ab54ce45 Refactor SelfUpdater adding L0 tests. (#1564)
* Refactor SelfUpdater with L0 tests.

* .

* .
2021-12-20 00:37:14 -05:00
Tingluo Huang
3e65909b81 Produce trimmed down runner packages. (#1556)
* Produce trimmed down runner packages.

* feedback.

* rename.
2021-12-15 22:05:58 -05:00
Tingluo Huang
3ec20e989d Update dependency check for dotnet 6. (#1551) 2021-12-15 12:16:11 -05:00
eric sciple
231fdcb19d bump patch version 2021-12-08 12:51:38 -06:00
eric sciple
bef164a12f revert node12 version due to fs.copyFileSync hang https://github.com/actions/runner/issues/1536 (#1537) 2021-12-06 10:27:44 -06:00
Meng Ye
a519f96a41 fix Log size and retention settings not work (#1507)
env
- RUNNER_LOGRETENTION
- WORKER_LOGRETENTION
- RUNNER _LOGSIZE
- WORKER _LOGSIZE
2021-12-02 10:04:39 -05:00
Tingluo Huang
b1ecffd707 Add masks for multiline secrets from ::add-mask:: (#1521)
* Add mask for multiline secrets.

* .
2021-12-01 09:53:13 -05:00
Tingluo Huang
801a02ec89 Bump runtime to dotnet 6 (#1471)
* bump runtime to dotnet 6
2021-11-30 22:00:15 -05:00
Ferenc Hammerl
6332f9a42f Prepare for runner 2.285.0 release (#1520) 2021-11-29 16:07:56 +00:00
Tingluo Huang
5b8ff174c6 Add telemetry around runner update process. (#1497)
* Add telemetry around runner update process.

* .

* .

* .
2021-11-22 18:27:57 -05:00
Tingluo Huang
e3e977fd84 Support node.js 16 and bump node.js 12 version. (#1439)
* Support node.js 16 and bump node.js 12 version.

* L0
2021-11-18 15:25:33 -05:00
Ferenc Hammerl
4dc8a09db3 Only execute post for actions that have one (#1481)
* Only execute post for actions that have one

* Revert haspost check

* Remove launch commit

* Remove comment

* Restore whitespace

* Restore wspace
2021-11-18 17:56:13 +01:00
Laura Yu
dcc5d34ad1 Add secret source to start job step (#1411)
* Add secret source to start job step

WIP

* Update to use GetGitHubContext to grab source info

* Update JobExtensionL0.cs

* Update JobExtension.cs

* Update JobExtension.cs
2021-11-17 17:09:38 -05:00
eric sciple
3e34fb10c1 improve telemetry to better diagnose runner configuration issues (#1487) 2021-11-15 13:42:57 -06:00
Tingluo Huang
23a693aa2c Update README.md 2021-11-09 14:19:46 -05:00
Tingluo Huang
eb36db8ff9 Try to delete portable-net45+win8 from all projects (#1470)
* Update Runner.Common.csproj

* Update Runner.Listener.csproj

* Update Runner.PluginHost.csproj

* Update Runner.Plugins.csproj

* Update Runner.Sdk.csproj

* Update Runner.Worker.csproj

* Update Sdk.csproj

* Update Test.csproj
2021-11-09 10:53:53 -05:00
Julio Barba
85e1927754 Prepare for runner 2.284.0 release (#1448) 2021-11-01 11:16:21 -04:00
Julio Barba
b6dbf42746 Improve retry handling based on feedback (#1447) 2021-10-29 16:04:34 -04:00
Ferenc Hammerl
67ba8a7d42 Support Conditional Steps in Composite Actions (#1438)
* conditional support for composite actions

* Fix Conditional function evaluation

* Push launch.json temporarily

* Revert "Push launch.json temporarily"

* rename context

* Cleanup comments

* fix success/failure functions to run based on pre/main steps

* idea of step_status

* change to use steps context, WIP

* add inputs to possible if condition expressions

* use action_status

* pr cleanup

* Added right stages

* Test on stage in conditional functions

* Fix naming and formatting

* Fix tests

* Add success and failure L0s

* Remove comment

* Remove whitespace

* Undo formatting

* Add L0 for step-if parsing

* Add ADR

Co-authored-by: Thomas Boop <thboop@github.com>
2021-10-29 15:45:42 +02:00
Ferenc Hammerl
e4f9e6ae26 Log current runner version in terminal (#1441) 2021-10-29 14:23:26 +02:00
Thomas Boop
854d5e3bf3 Fix an issue where nested local composite actions did not correctly register post steps (#1433)
* Always register post steps for local actions

* Register post steps along with their conditions

* remove debug code

Co-authored-by: Ferenc Hammerl <fhammerl@github.com>
2021-10-27 15:31:58 +02:00
Thomas Boop
57dec28f68 Cleanup Older versions on MacOS now that we recreate node versions as needed (#1410)
* Cleanup old version update code

* fix template

* fix indents
2021-10-19 10:15:37 -04:00
Tingluo Huang
55a861f089 Expose GITHUB_REF_* as environment variable (#1314)
* Keep env vars alphabetical

* ref_* context.

Co-authored-by: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com>
2021-10-18 22:22:34 -04:00
jeremyd2019
51b2031cbf Add arch to runner context (#1372)
Fixes #1185
2021-10-13 23:49:26 -04:00
Raphael Cruzeiro
400b2d879c Makes the user keychains available to the service (#847)
Without creating a session, the service is not able to access the keychains for the user specified under `UserName`. This causes any workflow that deals with code signing to fail as the only keychain loaded with be the system one. This should fix #350
2021-10-06 15:37:45 -04:00
Thomas Boop
c4b6d288d4 fix ephemeral runner upgrade on mac/linux (#1403) 2021-10-05 10:15:19 +02:00
Julio Barba
0699597876 Use Actions Service health and api.github.com endpoints after connection failure on Actions Server and Hosted (#1385) 2021-09-30 13:40:34 -04:00
Thomas Boop
a592b14ae3 Runner 2.283.2 Release (#1389) 2021-09-29 15:49:40 -04:00
Thomas Boop
04269f7b1b Handle keeping previous OSX versions more smoothly on Mac (#1381)
* Handle macOS upgrade smoothly

* cleanup

* misc cleanup

* final updates

* Update src/Misc/layoutbin/update.sh.template

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

* Update src/Misc/layoutbin/update.sh.template

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

* Upload telemetry and default to old method as needed

* minor fix

* add one more bit of logging

* some more telemetry

* quote variables to handle spaces

* tiny fix for ubuntu

* remove version and move telemetry to diag

* use full path

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2021-09-29 15:49:31 -04:00
Ferenc Hammerl
e89d2e84bd Stop-Commands: stopToken restrictions (#1371)
* Prevent stopTokens that are workflow commands

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

* Check context for env var too

* Accept true, 1 and $true instead of just "true"

* Setup ExpressionValues in tests

* Update src/Runner.Common/Constants.cs

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

* Separate success and fail tests for invalid token

* Fix envcontext for tests

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
2021-09-29 14:44:01 -04:00
Thomas Boop
afe7066e39 only cleanup runner local files on success (#1384) 2021-09-28 18:55:28 -04:00
Ferenc Hammerl
da79ef4acb Fix unconfiguring of runner after group changes (#1359)
* Ignore agentpool when unconfiguring the runner

Runner names and IDs are unique within a ServiceHost
They don't need to be included when unconfiguring the runner.

* Use -1 instead of 0 to highlight how it is ignored

* Use overloads and 0 instead of -1

Using 0 seems to be the convention

* Fix typo calling the wrong method
2021-09-22 15:04:43 +02:00
Tingluo Huang
5afb52b272 Update the comment about the --once in Constants.cs (#1360)
* Update Constants.cs

* feedback.

* Update src/Runner.Listener/Runner.cs

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
2021-09-21 21:31:48 +00:00
Thomas Boop
cf87c55557 Don't retry 422 (#1352) 2021-09-21 09:59:21 -04:00
Ferenc Hammerl
43fa351980 Update telemetry (#1355)
* Track "pause-logging"

* Bump release version
2021-09-20 15:54:20 +02:00
Ferenc Hammerl
ecfc2cc9e9 Prepare 2.283.0 release (#1351)
* Update releaseNote.md

* Update runnerversion

* Update releaseNote.md

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2021-09-20 14:59:37 +02:00
Ferenc Hammerl
740fb43731 Generic telemetry (#1321)
* Add generateIdTokenUrl as an env var

* Add generateIdTokenUrl to env vars

* Add basic telemetry class and submit it on jobcompleted

* Use constructor overload

* Rename telemetry to jobTelemetry

* Rename telemetry file

* Make JobTelemetryType a string

* Collect telemetry

* Remove debugger

* Update src/Runner.Worker/ActionCommandManager.cs

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

* Use same JobTelemetry for all contexts

* Mask telemetry data

* Mask in JobRunner instead

* Empty line

* Change method signature
Returning with a List suggests we clone it and that the
original doesn't change..

* Update launch.json

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
2021-09-20 14:44:50 +02:00
Patrick Ellis
f259e5706f Ephemeral runner deletes local .runner,.credentials files after completion (#1344)
Closes #1337
2021-09-16 11:00:27 -04:00
Ferenc Hammerl
5d84918ed5 Add name to runner context (#1312)
* Add generateIdTokenUrl as an env var

* Add generateIdTokenUrl to env vars

* Add name and runner_group to context

* No longer add runner-group

* Update runner name if needed

* Get interface instead of concrete class

* Check for nulls on ReservedAgent

* Avoid loading setting file unnecesseraly

* Only check agentName once

* Use Trace.Error when can't update settings

* Better equals and exception handling

* Update JobDispatcher.cs

* Add tests and null check
2021-09-16 15:25:51 +02:00
Julio Barba
881c521005 Revert "Recreate VssConnection on retry (#1316)" (#1343)
This reverts commit 4359dd605b.
2021-09-15 13:21:50 -04:00
Patrick Ellis
176e7f5208 Trim trailing whitespace in all md and yml files (#1329)
* Trim non-significant trailing whitespace, add final newlines to md,yml files

* Add .editorconfig with basic whitespace conventions
2021-09-15 13:35:25 +02:00
Jacob Wallraff
b6d46c148a Add attempt number to GitHub context (#1302)
* Add attempt number to GitHub context

* Change context name

* Changing order
2021-09-15 11:00:53 +02:00
Thomas Boop
38e33bb8e3 Update network.md 2021-09-14 15:28:30 -04:00
Patrick Ellis
404b3418b7 Prepare 2.282.0 release (#1327) 2021-09-13 13:56:47 -04:00
Tingluo Huang
7ffd9af644 Support --ephemeral flag (#660)
This optional flag will configure the runner to only take one job, and let the service un-configure the runner after that job finishes.
2021-09-13 11:28:09 -04:00
Thomas Boop
1b69c279f5 Networking TSG (#1325)
* Update Network Troubleshooting doc

* fix list

* Update network.md
2021-09-13 09:53:20 +02:00
Liviu Ionescu
567870dbb8 Avoid ConsoleColor.White, it is unreadable on light themes (#1295) (#1319)
* Avoid white, it is unreadable on light themes (#1295)

* remove ', ConsoleColor.White' from banner

* remove ', ConsoleColor.White' from prompt

* cleanups
2021-09-13 07:50:52 +00:00
Tingluo Huang
72fa2a8a0d Wait for job record updated before running steps. (#1320)
* Wait for job record updated before running steps.

* only oidc
2021-09-09 21:55:15 -04:00
Julio Barba
4359dd605b Recreate VssConnection on retry (#1316) 2021-09-09 19:09:17 -04:00
dependabot[bot]
aab936d081 Bump path-parse in /src/Misc/expressionFunc/hashFiles (#1256)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-01 16:48:41 -04:00
Nancy Hsieh
777ce5a0dc ignore empty arrays in jq select (#1269)
* ignore empty arrays in jq select

* Update delete.sh

Co-authored-by: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com>
2021-09-01 20:39:45 +00:00
Tingluo Huang
1a62162708 Prepare 2.281.1 runner release. (#1305) 2021-09-01 16:15:08 -04:00
Thomas Boop
9a829995e0 Show More Step Information in composite Actions (#1279) 2021-09-01 16:04:27 -04:00
Vladimir Panteleev
c5ce52641c Allow setting default severity to "notice" (#1213) 2021-09-01 16:01:23 -04:00
Nick Fields
e82725b580 Update error to say 'uninstall' not 'unconfigure' (#1179)
* Update error to say 'uninstall' not 'unconfigure'

* Say uninstall service in *nix config error msgs

Co-authored-by: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com>
2021-09-01 16:00:19 -04:00
Daniel Asztalos
0464f77de3 Typo fixed (#1289) 2021-09-01 15:59:18 -04:00
Tingluo Huang
1fc159e0df Temporary fix for macOS runner upgrade crash loop. (#1304) 2021-09-01 15:39:17 -04:00
Ferenc Hammerl
3615fb6923 Runner 2.281.0 (#1298)
* Add generateIdTokenUrl as an env var

* Add generateIdTokenUrl to env vars

* Update runnerversion

* Remove old relese notes

* Update releaseNote.md
2021-08-30 18:57:24 +02:00
Ferenc Hammerl
f61dcad5bb Don't try to login to ghcr.io with GHES tokens (#1291)
* Don't try GHXX tokens for ghcr.io login

* Explain hosted / onpremise in comment

* Nitfix variable name
2021-08-30 11:52:12 +02:00
Tingluo Huang
62d568674c Add ACTIONS_ID_TOKEN_REQUEST_URL/Token to script as well. (#1287) 2021-08-26 13:29:02 -04:00
Ferenc Hammerl
07c00f6a8a PowerShell secret masking (#1258)
* Trim pwsh special chars when masking secrets

* Add pwsh valueEncoder

* Explain regex

* Update ValueEncoders.cs

* Add tests for pwsh color codes in secrets

* Formatting

* Group tests into theories

* Split secret on PS chars and mask for them

* Clean up comments

* Remove unused unittest

* Rename escape methods
2021-08-25 23:07:19 +02:00
Tingluo Huang
05b84297b7 Add extra env for the Token log-in action is going to use to request ID_TOKEN. (#1270) 2021-08-23 14:50:35 -04:00
Thomas Boop
04679b56a9 Runner 2.280.3 Release (#1276) 2021-08-19 08:40:11 -04:00
Thomas Boop
d2ca24fa43 For Main Steps, just run the step, don't check condition (#1273)
* For Main Steps, just run the step, don't check condition

* fix whitespace

* pr feedback
2021-08-18 16:40:25 -04:00
Thomas Boop
abdaacfa6e Runner release 2.280.2 (#1259)
* Runner release 2.280.2

* update

* update
2021-08-12 12:55:45 -04:00
Thomas Boop
53fd7161e2 send path when resolving actions (#1250) 2021-08-11 09:48:32 -04:00
Ferenc Hammerl
ce68f3b167 Allow the use of flags in scripts/create-latest-svc.sh in a backwards compatible way (#1220)
* Use flags in svc creation script

* Refactor regex and add comments

* Fix indentation and typo in user matching

* Consistency use flags in automation scripts

* Update documentation to reflect new usage

* Make example more readable

* Remove test echos from script

* Remove test echo

* Format scripts and remove test script

* Remove tar

* Use getopts and single letter flags

* Update docs to show flag usage

* Update usage of create svc

* Revert svc to not use flags

* Revert delete script

* Update docs

* Readd deleted comments
2021-08-09 10:22:19 +02:00
Thomas Boop
e2c7329292 Release notes for 2.280.1 runner (#1244) 2021-08-04 13:28:32 -04:00
Thomas Boop
22a9d89772 Correctly set post step step context (#1243) 2021-08-04 11:39:22 -04:00
Thomas Boop
3851acd0cf fix continue on error (#1238) 2021-08-03 17:44:58 -04:00
Tingluo Huang
aab4aca8f7 Finish job when worker crashed with IOException. (#1239) 2021-08-03 16:21:39 -04:00
Thomas Boop
5af7b87074 Release notes for runner release 2.290.0 (#1237) 2021-08-03 11:12:43 -04:00
Tingluo Huang
110eb3a5de Add generateIdTokenUrl to env vars for actions. (#1234) 2021-08-02 14:47:50 -07:00
Tingluo Huang
bd1341e580 Print out resolved SHA for each action. (#1233) 2021-08-02 15:59:09 -04:00
Thomas Boop
85ce33b1d3 Composite Code Cleanup (#1232)
* composite polish

* Cleanup Condition Handling

* Refactor ConditionTraceWriter

* pr feedback

* cleanup
2021-08-02 14:57:25 -04:00
Thomas Boop
92ec3d0f29 Add better step telemetry and tracing for composite Actions (#1229)
* Add Step Telemetry

* better telemetry and tracing

* cleanup
2021-07-30 10:45:49 -04:00
Thomas Boop
4e95d0d6ad Support pre/post/container/composite actions within composite actions (#1222)
Support Composite Actions with uses: steps
2021-07-28 15:35:21 -04:00
Thomas Boop
5281434f3f Composite Actions Support ADR (#1144)
Composite Actions ADR
2021-07-27 09:29:50 -04:00
Ferenc Hammerl
e9a8bf29df Prefer higher libicu versions in installDependencies.sh (#1228)
* Update libicu dependencies

* Remove redundant comments (methodname is enough)
2021-07-27 14:30:53 +02:00
dependabot[bot]
a65331e887 Bump lodash in /src/Misc/expressionFunc/hashFiles (#1082)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-26 23:34:52 -04:00
dependabot[bot]
908a082527 Bump hosted-git-info in /src/Misc/expressionFunc/hashFiles (#1087)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-26 23:33:21 -04:00
dependabot[bot]
10ba74f59b Bump glob-parent in /src/Misc/expressionFunc/hashFiles (#1147)
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-26 22:55:26 -04:00
Ferenc Hammerl
33ee76df29 Prepare 2.279.0 Release (#1217)
* Bump runner version to 2.279.0

* Update release notes
2021-07-21 17:23:02 +02:00
Ferenc Hammerl
592ce1b230 Better error message when a command is not found (#1210)
* Describe missing util / command error in more detail

* Use more uniform language in traceInfo
2021-07-20 15:26:30 +02:00
Luke Tomlinson
fff31e11c5 Add notice annotation level and support more annotation fields (#1175)
* Add Notice Command

* Add Feature Flag For Enhanced Annotations

* Fix tests

* Add validation for columns and lines

* Fix order to match service

* Remove console.write

* Make Validation Better

* Cleanup

* Handle empty/whitespace strings

* Add more validation for line/column ranges

* Make Validation Debug, Not Throw

* Change casing to 🐫 from 🐍

* Give notice a well known tag

* Cleanup

* Sanitize invalid commands rather than fail
2021-07-13 11:38:16 -04:00
rethab
6443fe8c97 fix typos in docs of JobDispatcher (#1191)
* fix some typos in docs of JobDispatcher

* one more typo

* Update src/Runner.Listener/JobDispatcher.cs

Co-authored-by: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com>
2021-07-13 09:50:51 +02:00
Tingluo Huang
29c09c5bf8 ignore this test 2021-07-08 17:22:46 -04:00
Tingluo Huang
09821e2169 Check runner group when there is only default runner group. (#1172)
* Check runner group when there is only `default` runner group.

* L0
2021-07-02 12:31:54 -04:00
Thomas Boop
7c90b2a929 Composite Actions (#1170)
Composite Actions Refactoring
2021-07-01 13:34:28 -04:00
Tingluo Huang
ee34f4842e Delete runner-basic-e2e-test-case.yml 2021-06-21 13:46:21 -04:00
Tingluo Huang
713344016d Delete e2etest.yml 2021-06-21 13:21:24 -04:00
Darshan Sen
0a6c34669c send SIGKILL after 30s in gracefulShutdown() (#1156)
This resolves the TODO.

Fixes: https://github.com/actions/runner/issues/680
Signed-off-by: Darshan Sen <raisinten@gmail.com>
2021-06-21 12:07:39 -04:00
Tingluo Huang
40d6eb3da3 turn-off e2e test 2021-06-19 02:12:21 -04:00
RyotaK
34a985f3b9 Fix typo in sslcert.md (#1162) 2021-06-18 23:38:52 -04:00
jeff
42fe704132 Add message size in chars and bytes (#1100)
* Add message size in chars and bytes

* Log hash of message body
2021-06-10 15:58:56 -04:00
Ferenc Hammerl
a1bcd5996b Check if service exists before stopping it (Linux) (#1135)
* Check if service exists before stopping it

* Remove empty line (formatting)

* Use the same way as status to check service

* Revert formatting change
2021-06-08 13:27:19 +02:00
eric sciple
31584f4451 add comments (#1137) 2021-06-07 11:58:00 -04:00
Ferenc Hammerl
d4cdb633db Run config remove as user instead of root (#1127)
* Run config remove as user instead of root

* Explain why the runner can't be a service on a container

* Fix typo
2021-06-04 11:30:03 -04:00
Yann Soubeyrand
11939832df Return 1 on service status when service is not installed (#1018) 2021-06-04 10:52:13 -04:00
Rob Bos
ebadce7958 Typo: Changed dockerManger to dockerManager (#1133) 2021-06-04 10:51:30 -04:00
Tingluo Huang
4d5d5b74ee Ignore changes from BuildConstants.cs (#1132) 2021-06-03 16:19:26 -04:00
雪猫
ff12fae2c9 Fix automate script URL (#1089) 2021-06-02 08:47:51 -04:00
Ferenc Hammerl
8e907b19dc Vscode launch scripts (#1117)
* Stop ignoring .vscode (launch scripts)

* Check in launch scripts for config and run

This can cause an issue with existing launch configuration on the machines of contributors.

* Improve error msg when runner is not configured

* Unignore .vscode/launch and tasks only

* Remove stopAtEntry and add eof newline

* Remove Runner.Listener from error message

* Rename tasks and run configs

* Ignore BuildConstants.cs

* Use better error msg

* Explain development steps in depth

* Add launch config to directly debug worker

* Update docs with VS Code tips

* Remove auto-generated comments

* Fix link to quickstart in vscode.md

* Remove ':' from link to quickstart

* Revert "Ignore BuildConstants.cs"

This reverts commit 0f13922a87.

* Replace `.sh` with  `.(sh/cmd)` in docs
2021-06-02 08:34:16 -04:00
Ferenc Hammerl
93ec16e14f Wait for debugger to attach in Runner.Worker process (#1101)
* Wait for debugger to attach in Worker

Only if GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER is set

* Only wait if env variable parses to boolean 'true'

* Add 30s timeout to the wait

* Clean up leftover line

* Decrease wait to 20s

* Use ConvertToBoolean isntead of TryParse
2021-05-27 16:05:48 +02:00
Ferenc Hammerl
8863b1fb2c Add configure section to contribute.md (#1119)
Minor formatting improvements
2021-05-26 12:05:38 +02:00
Josh Soref
484ea74ed0 Update 0277-run-action-shell-options.md (#1106) 2021-05-26 12:04:38 +02:00
Josh Soref
f21e280b5c Update 0279-hashFiles-expression-function.md (#1107) 2021-05-26 12:04:24 +02:00
Josh Soref
e0643c694c Update 0361-wrapper-action.md (#1109) 2021-05-26 12:02:54 +02:00
Josh Soref
508d188fb6 Update 0397-runner-registration-labels.md (#1110) 2021-05-26 12:01:52 +02:00
Josh Soref
e7d74da160 Update 0549-composite-run-steps.md (#1111) 2021-05-26 12:01:02 +02:00
Josh Soref
d1f7258356 Update 0263-proxy-support.md (#1104)
Grammar fixes
2021-05-25 16:42:50 +02:00
eric sciple
3a5ab37153 Handle job not found when ensuring previous dispatch finished (#1083) 2021-05-11 09:19:23 -05:00
eric sciple
419ed24c1e rename nestedSteps to embeddedSteps (#1071) 2021-05-01 12:51:01 -05:00
eric sciple
7cc689b0d9 minor cleanup in composite (#1045) 2021-04-30 15:48:53 -05:00
Tingluo Huang
5941cceb7c Unset NODE_ICU_DATA before starting node in NodeHandler. (#1060)
* Unset NODE_ICU_DATA before starting node in NodeHandler.

* Update src/Runner.Worker/Handlers/NodeScriptActionHandler.cs

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>
2021-04-23 11:10:22 -04:00
Tingluo Huang
088caf5337 Update support platform doc for Apple M1 issue (#1057)
* apple M1 issue

* Update docs/start/envosx.md

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

* Update docs/start/envosx.md

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>
2021-04-23 09:47:30 -04:00
Ross Brodbeck
08852bd2fc these are not prerelease anymore 2021-04-23 05:26:31 -04:00
Tingluo Huang
57d694197f Not create runner release as pre-release. (#1054) 2021-04-21 17:08:44 -04:00
eric sciple
fc4027b3f1 Bump version (#1048) 2021-04-16 15:45:55 +00:00
eric sciple
d14881b970 do not trucate error message from template evaluation (#1038) 2021-04-06 16:45:40 -04:00
David Wolf
be9632302c Make FileShare ReadWrite (#1033)
* Make FileShare ReadWrite

* Update FileAccess to ReadWrite

* Update dotnet-install.ps1

* Update dotnet-install.ps1

* Update dotnet-install.ps1

* Update dotnet-install.sh
2021-04-01 16:54:23 -04:00
Ross Brodbeck
2b5ddd7c21 Add a default BuildConstants and change encoding on save (#1026) 2021-03-26 12:26:24 -04:00
eric sciple
8109c962f0 mask secrets with double-quotes when passed to docker command line (#1002) 2021-03-05 15:17:55 -06:00
Tim Etchells
af198237ca Delete script files before replacing during update (#984)
* Delete script files before replacing during update

Signed-off-by: Tim Etchells <tetchel@gmail.com>

* Use IOUtil.DelteFile()

Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2021-02-22 23:38:15 -05:00
Alberto Gimeno
1559ff15ec Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided (#990)
* Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided

* Use GITHUB_TOKEN also for containers in containers.pkg.github.com
2021-02-18 21:55:58 -05:00
Thomas Boop
67ff8d3460 Release 2.277.1 runner (#977)
* Revert "Enable tty output from Docker Actions (#916)"

5972bd0060

* Release notes

* add pr
2021-02-09 14:45:33 -05:00
172 changed files with 5354 additions and 3416 deletions

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
# https://editorconfig.org/
[*]
insert_final_newline = true # ensure all files end with a single newline
trim_trailing_whitespace = true # attempt to remove trailing whitespace on save
[*.md]
trim_trailing_whitespace = false # in markdown, "two trailing spaces" is unfortunately meaningful; it means `<br>`

6
.gitattributes vendored
View File

@@ -20,7 +20,7 @@
# #
# Merging from the command prompt will add diff markers to the files if there # Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS # are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following # the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat # file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user # these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below # intervention with every merge. To do so, just uncomment the entries below
@@ -70,9 +70,9 @@
############################################################################### ###############################################################################
# diff behavior for common document formats # diff behavior for common document formats
# #
# Convert binary document formats to text before diffing them. This feature # Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the # is only available from the command line. Turn it on by uncommenting the
# entries below. # entries below.
############################################################################### ###############################################################################
*.doc diff=astextplain *.doc diff=astextplain

View File

@@ -1,12 +1,18 @@
--- ---
name: Bug report name: 🛑 Report a bug in the runner application
about: Create a report to help us improve about: If you have issues with GitHub Actions, please follow the "support for GitHub Actions" link, below.
title: '' title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
<!--
👋 You're opening a bug report against the GitHub Actions **runner application**.
🛑 Please stop if you're not certain that the bug you're seeing is in the runner application - if you have general problems with actions, workflows, or runners, please see the [GitHub Community Support Forum](https://github.community/c/code-to-cloud/52) which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃
-->
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: ✅ Support for GitHub Actions
url: https://github.community/c/code-to-cloud/52
about: If you have questions about GitHub Actions or need support writing workflows, please ask in the GitHub Community Support forum.
- name: ✅ Feedback and suggestions for GitHub Actions
url: https://github.com/github/feedback/discussions/categories/actions-and-packages-feedback
about: If you have feedback or suggestions about GitHub Actions, please open a discussion (or add to an existing one) in the GitHub Actions Feedback. GitHub Actions Product Managers and Engineers monitor the feedback forum.
- name: ‼️ GitHub Security Bug Bounty
url: https://bounty.github.com/
about: Please report security vulnerabilities here.

View File

@@ -1,19 +1,24 @@
--- ---
name: Feature Request name: 🛑 Request a feature in the runner application
about: Create a request to help us improve about: If you have feature requests for GitHub Actions, please use the "feedback and suggestions for GitHub Actions" link below.
title: '' title: ''
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
Thank you 🙇‍♀ for wanting to create a feature in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. <!--
👋 You're opening a request for an enhancement in the GitHub Actions **runner application**.
🛑 Please stop if you're not certain that the feature you want is in the runner application - if you have a suggestion for improving GitHub Actions, please see the [GitHub Actions Feedback](https://github.com/github/feedback/discussions/categories/actions-and-packages-feedback) discussion forum which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃
Some additional useful links:
* If you have found a security issue [please submit it here](https://hackerone.com/github) * If you have found a security issue [please submit it here](https://hackerone.com/github)
* If you have questions or issues with the service, writing workflows or actions, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) * If you have questions or issues with the service, writing workflows or actions, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions)
* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support) * If you are having an issue or have a question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support)
If you have a feature request that is relevant to this repository, the runner, then please include the information below: If you have a feature request that is relevant to this repository, the runner, then please include the information below:
-->
**Describe the enhancement** **Describe the enhancement**
A clear and concise description of what the features or enhancement you need. A clear and concise description of what the features or enhancement you need.
@@ -24,4 +29,4 @@ If applicable, add a code snippet.
**Additional information** **Additional information**
Add any other context about the feature here. Add any other context about the feature here.
NOTE: if the feature request has been agreed upon then the assignee will create an ADR. See docs/adrs/README.md NOTE: if the feature request has been agreed upon then the assignee will create an ADR. See docs/adrs/README.md

View File

@@ -7,12 +7,12 @@ on:
- main - main
- releases/* - releases/*
paths-ignore: paths-ignore:
- '**.md' - '**.md'
pull_request: pull_request:
branches: branches:
- '*' - '*'
paths-ignore: paths-ignore:
- '**.md' - '**.md'
jobs: jobs:
build: build:
@@ -57,6 +57,29 @@ jobs:
working-directory: src working-directory: src
if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm' if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm'
# Check runtime/externals hash
- name: Compute/Compare runtime and externals Hash
shell: bash
run: |
echo "Current dotnet runtime hash result: $DOTNET_RUNTIME_HASH"
echo "Current Externals hash result: $EXTERNALS_HASH"
NeedUpdate=0
if [ "$EXTERNALS_HASH" != "$(cat ./src/Misc/contentHash/externals/${{ matrix.runtime }})" ] ;then
echo Hash mismatch, Update ./src/Misc/contentHash/externals/${{ matrix.runtime }} to $EXTERNALS_HASH
NeedUpdate=1
fi
if [ "$DOTNET_RUNTIME_HASH" != "$(cat ./src/Misc/contentHash/dotnetRuntime/${{ matrix.runtime }})" ] ;then
echo Hash mismatch, Update ./src/Misc/contentHash/dotnetRuntime/${{ matrix.runtime }} to $DOTNET_RUNTIME_HASH
NeedUpdate=1
fi
exit $NeedUpdate
env:
DOTNET_RUNTIME_HASH: ${{hashFiles('**/_layout_trims/runtime/**/*')}}
EXTERNALS_HASH: ${{hashFiles('**/_layout_trims/externals/**/*')}}
# 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'
@@ -67,7 +90,11 @@ jobs:
# Upload runner package tar.gz/zip as artifact # Upload runner package tar.gz/zip as artifact
- name: Publish Artifact - name: Publish Artifact
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v2
with: with:
name: runner-package-${{ matrix.runtime }} name: runner-package-${{ matrix.runtime }}
path: _package path: |
_package
_package_trims/trim_externals
_package_trims/trim_runtime
_package_trims/trim_runtime_externals

View File

@@ -1,7 +1,12 @@
name: "Code Scanning - Action" name: "Code Scanning - Action"
permissions:
security-events: write
on: on:
push: push:
branches:
- main
pull_request: pull_request:
schedule: schedule:
- cron: '0 0 * * 0' - cron: '0 0 * * 0'
@@ -28,7 +33,7 @@ jobs:
# languages: go, javascript, csharp, python, cpp, java # languages: go, javascript, csharp, python, cpp, java
- name: Manual build - name: Manual build
run : | run : |
./dev.sh layout Release linux-x64 ./dev.sh layout Release linux-x64
working-directory: src working-directory: src

View File

@@ -1,335 +0,0 @@
name: Runner E2E Test
on:
workflow_dispatch:
push:
branches:
- main
- releases/*
jobs:
init:
name: Initialize workflow ☕
runs-on: ubuntu-latest
outputs:
unique_runner_label: ${{steps.generator.outputs.runner_label}}
steps:
- name: Delete all runners
uses: actions/github-script@v3
with:
debug: true
script: |
var runnersResp = await github.actions.listSelfHostedRunnersForRepo({
owner: 'actions',
repo: 'runner',
per_page: '100'
});
for(var i=0; i<runnersResp.data.total_count; i++){
core.debug(JSON.stringify(runnersResp.data.runners[i]))
await github.actions.deleteSelfHostedRunnerFromRepo({
owner: 'actions',
repo: 'runner',
runner_id: runnersResp.data.runners[i].id
});
}
github-token: ${{secrets.PAT}}
- name: Generate Unique Runner label
id: generator
run: |
label=$(openssl rand -hex 16)
echo ::set-output name=runner_label::$label
build:
name: Build runner packages 🏗 📦
strategy:
matrix:
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
include:
- runtime: linux-x64
os: ubuntu-latest
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 }}
steps:
- uses: actions/checkout@v1
# Build runner layout
- name: Build & Layout Release
run: |
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
working-directory: src
# Create runner package tar.gz/zip
- name: Package Release
run: |
${{ matrix.devScript }} package Release ${{ matrix.runtime }}
working-directory: src
# Upload runner package tar.gz/zip as artifact
- name: Publish Artifact
uses: actions/upload-artifact@v1
with:
name: runner-package-${{ matrix.runtime }}
path: _package
dispatch_workflow:
name: Dispatch workflow to runners 🚨
needs: [init, build]
runs-on: ubuntu-latest
steps:
- name: Dispatch workflow
timeout-minutes: 10
uses: actions/github-script@v3
with:
debug: true
script: |
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
async function dispatchWorkflow(runner) {
await github.actions.createWorkflowDispatch({
owner: 'actions',
repo: 'runner',
workflow_id: 'runner-basic-e2e-test-case.yml',
ref: 'main',
inputs: {target_runner: runner}
});
}
var runWin64 = false, runLinux64 = false, runOsx64 = false, runLinuxARM64 = false;
while (true) {
core.info(`------------- Waiting for runners to be configured --------------`)
await sleep(10000);
var runnersResp = await github.actions.listSelfHostedRunnersForRepo({owner: 'actions', repo: 'runner', per_page: '100'});
for (var i = 0; i < runnersResp.data.total_count; i++) {
core.debug(JSON.stringify(runnersResp.data.runners[i]))
var labels = runnersResp.data.runners[i].labels;
for (var j = 0; j < labels.length; j++) {
core.debug(`Comparing: ${labels[j].name} to win-x64/linux-x64/osx-x64/linux-arm64-${{ needs.init.outputs.unique_runner_label }}`)
if (labels[j].name == 'win-x64-${{needs.init.outputs.unique_runner_label}}' && runWin64 == false) {
core.info(`------------------- Windows runner is configured, queue Windows Run -------------------------`)
runWin64 = true;
await dispatchWorkflow('win-x64-${{needs.init.outputs.unique_runner_label}}');
break;
} else if (labels[j].name == 'linux-x64-${{needs.init.outputs.unique_runner_label}}' && runLinux64 == false) {
core.info(`------------------- Linux runner is configured, queue Linux Run -------------------------`)
runLinux64 = true;
await dispatchWorkflow('linux-x64-${{needs.init.outputs.unique_runner_label}}');
break;
} else if (labels[j].name == 'osx-x64-${{needs.init.outputs.unique_runner_label}}' && runOsx64 == false) {
core.info(`------------------- macOS runner is configured, queue macOS Run -------------------------`)
runOsx64 = true;
await dispatchWorkflow('osx-x64-${{needs.init.outputs.unique_runner_label}}');
break;
} else if (labels[j].name == 'linux-arm64-${{needs.init.outputs.unique_runner_label}}' && runLinuxARM64 == false) {
core.info(`------------------- Linux ARM64 runner is configured, queue Linux ARM64 Run-------------------------`)
runLinuxARM64 = true;
await dispatchWorkflow('linux-arm64-${{needs.init.outputs.unique_runner_label}}');
break;
}
}
}
if (runWin64 && runLinux64 && runOsx64 && runLinuxARM64) {
core.info(`--------------------- ALL runner are running jobs --------------------------`)
break;
} else {
core.info(`---------- Windows running: ${runWin64} -- Linux running: ${runLinux64} -- macOS running: ${runOsx64} -- Linux ARM64 running: ${runLinuxARM64} -----------`)
}
}
github-token: ${{secrets.PAT}}
LinuxE2E:
needs: [build, init]
runs-on: ubuntu-latest
steps:
- name: Download Runner
uses: actions/download-artifact@v2
with:
name: runner-package-linux-x64
- name: Unzip Runner Package
run: |
tar -xzf *.tar.gz
- name: Configure Runner
env:
unique_runner_name: linux-x64-${{needs.init.outputs.unique_runner_label}}
run: |
./config.sh --url ${{github.event.repository.html_url}} --unattended --name $unique_runner_name --pat ${{secrets.PAT}} --labels $unique_runner_name --replace
- name: Start Runner and Wait for Job
timeout-minutes: 5
run: |
./run.sh --once
- name: Remove Runner
if: always()
continue-on-error: true
run: |
./config.sh remove --pat ${{secrets.PAT}}
- name: Upload Runner Logs
if: always()
uses: actions/upload-artifact@v2
with:
name: linux_x64_logs
path: _diag
macOSE2E:
needs: [build, init]
runs-on: macos-latest
steps:
- name: Download Runner
uses: actions/download-artifact@v2
with:
name: runner-package-osx-x64
- name: Unzip Runner Package
run: |
tar -xzf *.tar.gz
- name: Configure Runner
env:
unique_runner_name: osx-x64-${{needs.init.outputs.unique_runner_label}}
run: |
./config.sh --url ${{github.event.repository.html_url}} --unattended --name $unique_runner_name --pat ${{secrets.PAT}} --labels $unique_runner_name --replace
- name: Start Runner and Wait for Job
timeout-minutes: 5
run: |
./run.sh --once
- name: Remove Runner
if: always()
continue-on-error: true
run: |
./config.sh remove --pat ${{secrets.PAT}}
- name: Upload Runner Logs
if: always()
uses: actions/upload-artifact@v2
with:
name: osx_x64_logs
path: _diag
ARM64E2E:
needs: [build, init]
runs-on: ubuntu-latest
steps:
- name: Download Runner
uses: actions/download-artifact@v2
with:
name: runner-package-linux-arm64
- name: Unzip Runner Package
run: |
tar -xzf *.tar.gz
- name: Prepare QEMU
run: |
docker run --rm --privileged multiarch/qemu-user-static:register --reset
- name: Configure Runner
uses: docker://multiarch/ubuntu-core:arm64-bionic
with:
args: 'bash -c "apt-get update && apt-get install -y curl && ./bin/installdependencies.sh && ./config.sh --unattended --name $unique_runner_name --url ${{github.event.repository.html_url}} --pat ${{secrets.PAT}} --labels $unique_runner_name --replace"'
env:
RUNNER_ALLOW_RUNASROOT: 1
unique_runner_name: linux-arm64-${{needs.init.outputs.unique_runner_label}}
- name: Start Runner and Wait for Job
timeout-minutes: 5
uses: docker://multiarch/ubuntu-core:arm64-bionic
with:
args: 'bash -c "apt-get update && apt-get install -y curl git && ./bin/installdependencies.sh && ./run.sh --once"'
env:
RUNNER_ALLOW_RUNASROOT: 1
- name: Remove Runner
if: always()
continue-on-error: true
uses: docker://multiarch/ubuntu-core:arm64-bionic
with:
args: 'bash -c "apt-get update && apt-get install -y curl && ./bin/installdependencies.sh && ./config.sh remove --pat ${{secrets.PAT}}"'
env:
RUNNER_ALLOW_RUNASROOT: 1
- name: Upload Runner Logs
if: always()
uses: actions/upload-artifact@v2
with:
name: linux_arm64_logs
path: _diag
WindowsE2E:
needs: [build, init]
runs-on: windows-latest
steps:
- name: Download Runner
uses: actions/download-artifact@v2
with:
name: runner-package-win-x64
- name: Unzip Runner Package
run: |
Get-ChildItem *.zip | Expand-Archive -DestinationPath $PWD
- name: Configure Runner
shell: cmd
run: |
config.cmd --unattended --url ${{github.event.repository.html_url}} --name %unique_runner_name% --pat ${{secrets.PAT}} --labels %unique_runner_name% --replace
env:
unique_runner_name: win-x64-${{needs.init.outputs.unique_runner_label}}
- name: Start Runner and Wait for Job
shell: cmd
timeout-minutes: 5
run: |
run.cmd --once
- name: Remove Runner
shell: cmd
if: always()
continue-on-error: true
run: |
config.cmd remove --pat ${{secrets.PAT}}
- name: Upload Runner Logs
if: always()
uses: actions/upload-artifact@v2
with:
name: win_x64_logs
path: _diag
check:
name: Check runner logs 🕵️‍♂️
needs: [WindowsE2E, LinuxE2E, macOSE2E, ARM64E2E]
runs-on: ubuntu-latest
steps:
- name: Download Linux Runner Logs
uses: actions/download-artifact@v2
with:
name: linux_x64_logs
path: linux_x64_logs
- name: Download macOS Runner Logs
uses: actions/download-artifact@v2
with:
name: osx_x64_logs
path: osx_x64_logs
- name: Download Linux ARM64 Runner Logs
uses: actions/download-artifact@v2
with:
name: linux_arm64_logs
path: linux_arm64_logs
- name: Download Windows Runner Logs
uses: actions/download-artifact@v2
with:
name: win_x64_logs
path: win_x64_logs
- name: Check Runner Logs
run: |
function failed()
{
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
grep -R "completed with result: Succeeded" ./win_x64_logs || failed "Windows Runner fail to run the job, please check logs"
grep -R "completed with result: Succeeded" ./linux_x64_logs || failed "Linux Runner fail to run the job, please check logs"
grep -R "completed with result: Succeeded" ./osx_x64_logs || failed "macOS Runner fail to run the job, please check logs"
grep -R "completed with result: Succeeded" ./linux_arm64_logs || failed "Linux ARM64 Runner fail to run the job, please check logs"

View File

@@ -5,7 +5,7 @@ on:
push: push:
paths: paths:
- releaseVersion - releaseVersion
jobs: jobs:
check: check:
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main' if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
@@ -13,8 +13,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Make sure ./releaseVersion match ./src/runnerversion # Make sure ./releaseVersion match ./src/runnerversion
# Query GitHub release ensure version is not used # Query GitHub release ensure version is not used
- name: Check version - name: Check version
uses: actions/github-script@0.3.0 uses: actions/github-script@0.3.0
with: with:
@@ -42,7 +42,7 @@ jobs:
throw e throw e
} }
} }
build: build:
needs: check needs: check
outputs: outputs:
@@ -51,6 +51,21 @@ jobs:
linux-arm-sha: ${{ steps.sha.outputs.linux-arm-sha256 }} linux-arm-sha: ${{ steps.sha.outputs.linux-arm-sha256 }}
win-x64-sha: ${{ steps.sha.outputs.win-x64-sha256 }} win-x64-sha: ${{ steps.sha.outputs.win-x64-sha256 }}
osx-x64-sha: ${{ steps.sha.outputs.osx-x64-sha256 }} osx-x64-sha: ${{ steps.sha.outputs.osx-x64-sha256 }}
linux-x64-sha-noexternals: ${{ steps.sha_noexternals.outputs.linux-x64-sha256 }}
linux-arm64-sha-noexternals: ${{ steps.sha_noexternals.outputs.linux-arm64-sha256 }}
linux-arm-sha-noexternals: ${{ steps.sha_noexternals.outputs.linux-arm-sha256 }}
win-x64-sha-noexternals: ${{ steps.sha_noexternals.outputs.win-x64-sha256 }}
osx-x64-sha-noexternals: ${{ steps.sha_noexternals.outputs.osx-x64-sha256 }}
linux-x64-sha-noruntime: ${{ steps.sha_noruntime.outputs.linux-x64-sha256 }}
linux-arm64-sha-noruntime: ${{ steps.sha_noruntime.outputs.linux-arm64-sha256 }}
linux-arm-sha-noruntime: ${{ steps.sha_noruntime.outputs.linux-arm-sha256 }}
win-x64-sha-noruntime: ${{ steps.sha_noruntime.outputs.win-x64-sha256 }}
osx-x64-sha-noruntime: ${{ steps.sha_noruntime.outputs.osx-x64-sha256 }}
linux-x64-sha-noruntime-noexternals: ${{ steps.sha_noruntime_noexternals.outputs.linux-x64-sha256 }}
linux-arm64-sha-noruntime-noexternals: ${{ steps.sha_noruntime_noexternals.outputs.linux-arm64-sha256 }}
linux-arm-sha-noruntime-noexternals: ${{ steps.sha_noruntime_noexternals.outputs.linux-arm-sha256 }}
win-x64-sha-noruntime-noexternals: ${{ steps.sha_noruntime_noexternals.outputs.win-x64-sha256 }}
osx-x64-sha-noruntime-noexternals: ${{ steps.sha_noruntime_noexternals.outputs.osx-x64-sha256 }}
strategy: strategy:
matrix: matrix:
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ] runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
@@ -99,14 +114,6 @@ jobs:
${{ matrix.devScript }} package Release ${{ matrix.runtime }} ${{ matrix.devScript }} package Release ${{ matrix.runtime }}
working-directory: src working-directory: src
# Upload runner package tar.gz/zip as artifact.
# Since each package name is unique, so we don't need to put ${{matrix}} info into artifact name
- name: Publish Artifact
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v1
with:
name: runner-packages
path: _package
# compute shas and set as job outputs to use in release notes # compute shas and set as job outputs to use in release notes
- run: brew install coreutils #needed for shasum util - run: brew install coreutils #needed for shasum util
if: ${{ matrix.os == 'macOS-latest' }} if: ${{ matrix.os == 'macOS-latest' }}
@@ -120,6 +127,91 @@ jobs:
id: sha id: sha
name: Compute SHA256 name: Compute SHA256
working-directory: _package working-directory: _package
- run: |
file=$(ls)
sha=$(sha256sum $file | awk '{ print $1 }')
echo "Computed sha256: $sha for $file"
echo "::set-output name=${{matrix.runtime}}-sha256::$sha"
echo "::set-output name=sha256::$sha"
shell: bash
id: sha_noexternals
name: Compute SHA256
working-directory: _package_trims/trim_externals
- run: |
file=$(ls)
sha=$(sha256sum $file | awk '{ print $1 }')
echo "Computed sha256: $sha for $file"
echo "::set-output name=${{matrix.runtime}}-sha256::$sha"
echo "::set-output name=sha256::$sha"
shell: bash
id: sha_noruntime
name: Compute SHA256
working-directory: _package_trims/trim_runtime
- run: |
file=$(ls)
sha=$(sha256sum $file | awk '{ print $1 }')
echo "Computed sha256: $sha for $file"
echo "::set-output name=${{matrix.runtime}}-sha256::$sha"
echo "::set-output name=sha256::$sha"
shell: bash
id: sha_noruntime_noexternals
name: Compute SHA256
working-directory: _package_trims/trim_runtime_externals
- name: Create trimmedpackages.json for ${{ matrix.runtime }}
if: matrix.runtime == 'win-x64'
uses: actions/github-script@0.3.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const core = require('@actions/core')
const fs = require('fs');
const runnerVersion = fs.readFileSync('src/runnerversion', 'utf8').replace(/\n$/g, '')
var trimmedPackages = fs.readFileSync('src/Misc/trimmedpackages_zip.json', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion).replace(/<RUNNER_PLATFORM>/g, '${{ matrix.runtime }}')
trimmedPackages = trimmedPackages.replace(/<RUNTIME_HASH>/g, '${{hashFiles('**/_layout_trims/runtime/**/*')}}')
trimmedPackages = trimmedPackages.replace(/<EXTERNALS_HASH>/g, '${{hashFiles('**/_layout_trims/externals/**/*')}}')
trimmedPackages = trimmedPackages.replace(/<NO_RUNTIME_EXTERNALS_HASH>/g, '${{steps.sha_noruntime_noexternals.outputs.sha256}}')
trimmedPackages = trimmedPackages.replace(/<NO_RUNTIME_HASH>/g, '${{steps.sha_noruntime.outputs.sha256}}')
trimmedPackages = trimmedPackages.replace(/<NO_EXTERNALS_HASH>/g, '${{steps.sha_noexternals.outputs.sha256}}')
console.log(trimmedPackages)
fs.writeFileSync('${{ matrix.runtime }}-trimmedpackages.json', trimmedPackages)
- name: Create trimmedpackages.json for ${{ matrix.runtime }}
if: matrix.runtime != 'win-x64'
uses: actions/github-script@0.3.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const core = require('@actions/core')
const fs = require('fs');
const runnerVersion = fs.readFileSync('src/runnerversion', 'utf8').replace(/\n$/g, '')
var trimmedPackages = fs.readFileSync('src/Misc/trimmedpackages_targz.json', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion).replace(/<RUNNER_PLATFORM>/g, '${{ matrix.runtime }}')
trimmedPackages = trimmedPackages.replace(/<RUNTIME_HASH>/g, '${{hashFiles('**/_layout_trims/runtime/**/*')}}')
trimmedPackages = trimmedPackages.replace(/<EXTERNALS_HASH>/g, '${{hashFiles('**/_layout_trims/externals/**/*')}}')
trimmedPackages = trimmedPackages.replace(/<NO_RUNTIME_EXTERNALS_HASH>/g, '${{steps.sha_noruntime_noexternals.outputs.sha256}}')
trimmedPackages = trimmedPackages.replace(/<NO_RUNTIME_HASH>/g, '${{steps.sha_noruntime.outputs.sha256}}')
trimmedPackages = trimmedPackages.replace(/<NO_EXTERNALS_HASH>/g, '${{steps.sha_noexternals.outputs.sha256}}')
console.log(trimmedPackages)
fs.writeFileSync('${{ matrix.runtime }}-trimmedpackages.json', trimmedPackages)
# Upload runner package tar.gz/zip as artifact.
# Since each package name is unique, so we don't need to put ${{matrix}} info into artifact name
- name: Publish Artifact
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v2
with:
name: runner-packages
path: |
_package
_package_trims/trim_externals
_package_trims/trim_runtime
_package_trims/trim_runtime_externals
${{ matrix.runtime }}-trimmedpackages.json
release: release:
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -150,9 +242,24 @@ jobs:
releaseNote = releaseNote.replace(/<LINUX_X64_SHA>/g, '${{needs.build.outputs.linux-x64-sha}}') releaseNote = releaseNote.replace(/<LINUX_X64_SHA>/g, '${{needs.build.outputs.linux-x64-sha}}')
releaseNote = releaseNote.replace(/<LINUX_ARM_SHA>/g, '${{needs.build.outputs.linux-arm-sha}}') releaseNote = releaseNote.replace(/<LINUX_ARM_SHA>/g, '${{needs.build.outputs.linux-arm-sha}}')
releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA>/g, '${{needs.build.outputs.linux-arm64-sha}}') releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA>/g, '${{needs.build.outputs.linux-arm64-sha}}')
releaseNote = releaseNote.replace(/<WIN_X64_SHA_NOEXTERNALS>/g, '${{needs.build.outputs.win-x64-sha-noexternals}}')
releaseNote = releaseNote.replace(/<OSX_X64_SHA_NOEXTERNALS>/g, '${{needs.build.outputs.osx-x64-sha-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_X64_SHA_NOEXTERNALS>/g, '${{needs.build.outputs.linux-x64-sha-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_ARM_SHA_NOEXTERNALS>/g, '${{needs.build.outputs.linux-arm-sha-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA_NOEXTERNALS>/g, '${{needs.build.outputs.linux-arm64-sha-noexternals}}')
releaseNote = releaseNote.replace(/<WIN_X64_SHA_NORUNTIME>/g, '${{needs.build.outputs.win-x64-sha-noruntime}}')
releaseNote = releaseNote.replace(/<OSX_X64_SHA_NORUNTIME>/g, '${{needs.build.outputs.osx-x64-sha-noruntime}}')
releaseNote = releaseNote.replace(/<LINUX_X64_SHA_NORUNTIME>/g, '${{needs.build.outputs.linux-x64-sha-noruntime}}')
releaseNote = releaseNote.replace(/<LINUX_ARM_SHA_NORUNTIME>/g, '${{needs.build.outputs.linux-arm-sha-noruntime}}')
releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA_NORUNTIME>/g, '${{needs.build.outputs.linux-arm64-sha-noruntime}}')
releaseNote = releaseNote.replace(/<WIN_X64_SHA_NORUNTIME_NOEXTERNALS>/g, '${{needs.build.outputs.win-x64-sha-noruntime-noexternals}}')
releaseNote = releaseNote.replace(/<OSX_X64_SHA_NORUNTIME_NOEXTERNALS>/g, '${{needs.build.outputs.osx-x64-sha-noruntime-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_X64_SHA_NORUNTIME_NOEXTERNALS>/g, '${{needs.build.outputs.linux-x64-sha-noruntime-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_ARM_SHA_NORUNTIME_NOEXTERNALS>/g, '${{needs.build.outputs.linux-arm-sha-noruntime-noexternals}}')
releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA_NORUNTIME_NOEXTERNALS>/g, '${{needs.build.outputs.linux-arm64-sha-noruntime-noexternals}}')
console.log(releaseNote) console.log(releaseNote)
core.setOutput('version', runnerVersion); core.setOutput('version', runnerVersion);
core.setOutput('note', releaseNote); core.setOutput('note', releaseNote);
# Create GitHub release # Create GitHub release
- uses: actions/create-release@master - uses: actions/create-release@master
id: createRelease id: createRelease
@@ -164,16 +271,15 @@ jobs:
release_name: "v${{ steps.releaseNote.outputs.version }}" release_name: "v${{ steps.releaseNote.outputs.version }}"
body: | body: |
${{ steps.releaseNote.outputs.note }} ${{ steps.releaseNote.outputs.note }}
prerelease: true
# Upload release assets # Upload release assets (full runner packages)
- name: Upload Release Asset (win-x64) - name: Upload Release Asset (win-x64)
uses: actions/upload-release-asset@v1.0.1 uses: actions/upload-release-asset@v1.0.1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.createRelease.outputs.upload_url }} upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip asset_path: ${{ github.workspace }}/_package/actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
@@ -183,7 +289,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.createRelease.outputs.upload_url }} upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_path: ${{ github.workspace }}/_package/actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
@@ -193,7 +299,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.createRelease.outputs.upload_url }} upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_path: ${{ github.workspace }}/_package/actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
@@ -203,7 +309,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.createRelease.outputs.upload_url }} upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.tar.gz asset_path: ${{ github.workspace }}/_package/actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.tar.gz asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
@@ -213,6 +319,210 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.createRelease.outputs.upload_url }} upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_path: ${{ github.workspace }}/_package/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
# Upload release assets (trim externals)
- name: Upload Release Asset (win-x64-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_externals/actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noexternals.zip
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noexternals.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-x64-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_externals/actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (osx-x64-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_externals/actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_externals/actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm64-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_externals/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noexternals.tar.gz
asset_content_type: application/octet-stream
# Upload release assets (trim runtime)
- name: Upload Release Asset (win-x64-noruntime)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime/actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noruntime.zip
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noruntime.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-x64-noruntime)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime/actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (osx-x64-noruntime)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime/actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm-noruntime)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime/actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm64-noruntime)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noruntime.tar.gz
asset_content_type: application/octet-stream
# Upload release assets (trim runtime and externals)
- name: Upload Release Asset (win-x64-noruntime-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime_externals/actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.zip
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-x64-noruntime-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime_externals/actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (osx-x64-noruntime-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime_externals/actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm-noruntime-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime_externals/actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm64-noruntime-noexternals)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/_package_trims/trim_runtime_externals/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-noruntime-noexternals.tar.gz
asset_content_type: application/octet-stream
# Upload release assets (trimmedpackages.json)
- name: Upload Release Asset (win-x64-trimmedpackages.json)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/win-x64-trimmedpackages.json
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-x64-trimmedpackages.json)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/linux-x64-trimmedpackages.json
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream
- name: Upload Release Asset (osx-x64-trimmedpackages.json)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/osx-x64-trimmedpackages.json
asset_name: actions-runner-osx-x64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm-trimmedpackages.json)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/linux-arm-trimmedpackages.json
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm64-trimmedpackages.json)
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/linux-arm64-trimmedpackages.json
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}-trimmedpackages.json
asset_content_type: application/octet-stream

View File

@@ -1,31 +0,0 @@
name: Runner Basics Test Case
on:
workflow_dispatch:
inputs:
target_runner:
description: 'Self-hosted runner will run the job'
required: true
jobs:
test:
runs-on:
- self-hosted
- ${{github.event.inputs.target_runner}}
name: Runner Basic Test 🛠
steps:
- uses: actions/checkout@v2
- name: Run a one-line script
run: echo Hello, world!
- name: Run a multi-line script
shell: bash
run: |
printenv|sort
cat $GITHUB_EVENT_PATH
- name: Validate GitHub Context
shell: bash
run: |
declare -a context_vars=("GITHUB_ACTION" "GITHUB_ACTIONS" "GITHUB_REPOSITORY" "GITHUB_WORKSPACE" "GITHUB_SHA" "GITHUB_RUN_ID" "GITHUB_RUN_NUMBER")
for var in ${context_vars[@]};
do [ -z "${!var}" ] && echo "##[error]$var not found" && exit 1 || echo "$var: ${!var}"; done

11
.gitignore vendored
View File

@@ -8,21 +8,22 @@
**/*.xproj **/*.xproj
**/*.xproj.user **/*.xproj.user
**/.vs **/.vs
**/.vscode
**/*.error **/*.error
**/*.json.pretty **/*.json.pretty
.idea/ .idea/
.vscode
!.vscode/launch.json
!.vscode/tasks.json
# output # output
node_modules node_modules
_downloads _downloads
_layout _layout
_layout_trims
_package _package
_package_trims
_dotnetsdk _dotnetsdk
TestResults TestResults
TestLogs TestLogs
.DS_Store .DS_Store
**/*.DotSettings.user **/*.DotSettings.user
#generated
src/Runner.Sdk/BuildConstants.cs

57
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,57 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run [build]",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build runner layout",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Run",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Configure",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "create runner layout",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"configure"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Debug Worker",
"type": "coreclr",
"request": "attach",
"processName": "Runner.Worker",
"requireExactSource": false,
},
{
"name": "Attach Debugger",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}",
"requireExactSource": false,
},
],
}

33
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "create runner layout",
"detail": "Build and Copy all projects, scripts and external dependencies to _layout from src (run this the first time or after deleting _layout)",
"command": "./dev.sh",
"windows": {
"command": "dev.cmd"
},
"args": [
"layout"
],
"options": {
"cwd": "${workspaceFolder}/src"
},
},
{
"label": "build runner layout",
"detail": "Build and Copy all projects to _layout from src (run this on code change)",
"command": "./dev.sh",
"windows": {
"command": "dev.cmd"
},
"args": [
"build"
],
"options": {
"cwd": "${workspaceFolder}/src"
},
}
],
}

View File

@@ -5,7 +5,7 @@
# GitHub Actions Runner # GitHub Actions Runner
[![Actions Status](https://github.com/actions/runner/workflows/Runner%20CI/badge.svg)](https://github.com/actions/runner/actions) [![Actions Status](https://github.com/actions/runner/workflows/Runner%20CI/badge.svg)](https://github.com/actions/runner/actions)
[![Runner E2E Test](https://github.com/actions/runner/workflows/Runner%20E2E%20Test/badge.svg)](https://github.com/actions/runner/actions) [![Runner E2E Test](https://github.com/actions-canary/actions-runner-e2e/actions/workflows/runner_e2etest.yml/badge.svg)](https://github.com/actions-canary/actions-runner-e2e/actions/workflows/runner_e2etest.yml)
The runner is the application that runs a job from a GitHub Actions workflow. It is used by GitHub Actions in the [hosted virtual environments](https://github.com/actions/virtual-environments), or you can [self-host the runner](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners) in your own environment. The runner is the application that runs a job from a GitHub Actions workflow. It is used by GitHub Actions in the [hosted virtual environments](https://github.com/actions/virtual-environments), or you can [self-host the runner](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners) in your own environment.

View File

@@ -1,4 +1,4 @@
# ADR 263: Self Hosted Runner Proxies # ADR 263: Self-Hosted Runner Proxies
**Date**: 2019-11-13 **Date**: 2019-11-13
@@ -6,13 +6,13 @@
## Context ## Context
- Proxy support is required for some enterprises and organizations to start using their own self hosted runners - Proxy support is required for some enterprises and organizations to start using their own self-hosted runners
- While there is not a standard convention, many applications support setting proxies via the environmental variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, R, ect - While there is not a standard convention, many applications support setting proxies via the environment variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, and R
- Some of these applications use `HTTPS_PROXY` versus `https_proxy`, but most understand or primarily support the lowercase variant - Some of these applications use `HTTPS_PROXY` versus `https_proxy`, but most understand or primarily support the lowercase variant
## Decision ## Decision
We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy` and `no_proxy` if they are set. We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy`, and `no_proxy` if they are set.
These are described in detail below: These are described in detail below:
- `https_proxy` a proxy URL for all https traffic. It may contain basic authentication credentials. For example: - `https_proxy` a proxy URL for all https traffic. It may contain basic authentication credentials. For example:
- http://proxy.com - http://proxy.com
@@ -22,20 +22,20 @@ These are described in detail below:
- http://proxy.com - http://proxy.com
- http://127.0.0.1:8080 - http://127.0.0.1:8080
- http://user:password@proxy.com - http://user:password@proxy.com
- `no_proxy` a comma separated list of hosts that should not use the proxy. An optional port may be specified - `no_proxy` a comma-separated list of hosts that should not use the proxy. An optional port may be specified. For example:
- `google.com` - `google.com`
- `yahoo.com:443` - `yahoo.com:443`
- `google.com,bing.com` - `google.com,bing.com`
We won't use `http_proxy` for https traffic when `https_proxy` is not set, this behavior lines up with any libcurl based tools (curl, git) and wget. We won't use `http_proxy` for https traffic when `https_proxy` is not set, this behavior lines up with any libcurl based tools (curl, git) and wget.
Otherwise action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts. Otherwise, action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts.
Example: Example:
Customer set `http_proxy=http://127.0.0.1:8888` and configure the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic. Customer sets `http_proxy=http://127.0.0.1:8888` and configures the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic.
> `golang`, `node.js` and other dev tools from the linux community use `http_proxy` for both http and https traffic based on my research. > `golang`, `node.js`, and other dev tools from the Linux community use `http_proxy` for both http and https traffic based on my research.
A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self hosted runners to set up proxy, and expose it in a way users are already familiar with. A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self-hosted runners to set up a proxy and expose it in a way users are already familiar with.
A password provided for a proxy will be masked in the logs. A password provided for a proxy will be masked in the logs.
@@ -43,19 +43,19 @@ We will support the lowercase and uppercase variants, with lowercase taking prio
### No Proxy Format ### No Proxy Format
While exact implementations are different per application on handle `no_proxy` env, most applications accept a comma separated list of hosts. Some accept wildcard characters (*). We are going to do exact case-insensitive matches, and not support wildcards at this time. While exact implementations are different per application on handling `no_proxy` env, most applications accept a comma-separated list of hosts. Some accept wildcard characters (`*`). We are going to do exact case-insensitive matches, and not support wildcards at this time.
For example: For example:
- example.com will match example.com, foo.example.com, foo.bar.example.com - `example.com` will match `example.com`, `foo.example.com`, and `foo.bar.example.com`
- foo.example.com will match bar.foo.example.com and foo.example.com - `foo.example.com` will match `bar.foo.example.com` and `foo.example.com`
We will not support IP addresses for `no_proxy`, only hostnames. We will not support IP addresses for `no_proxy`, only hostnames.
## Consequences ## Consequences
1. Enterprises and organizations needing proxy support will be able to embrace self hosted runners 1. Enterprises and organizations needing proxy support will be able to embrace self-hosted runners
2. Users will need to set these environmental variables before configuring the runner in order to use a proxy when configuring 2. Users will need to set these environment variables before configuring the runner in order to use a proxy when configuring
3. The runner will read from the environmental variables during config and runtime and use the provided proxy if it exists 3. The runner will read from the environment variables during config and runtime and use the provided proxy if it exists
4. Users may need to pass these environmental variables into other applications if they do not natively take these variables 4. Users may need to pass these environment variables into other applications if they do not natively take these variables
5. Action authors may need to update their workflows to react to the these environment variables 5. Action authors may need to update their workflows to react to these environment variables
6. We will document the way of setting environmental variables for runners using the environment variables and how the runner uses them 6. We will document the way of setting environment variables for runners using the environment variables and how the runner uses them
7. Like all other secrets, users will be able to relatively easily figure out proxy password if they can modify a workflow file running on a self hosted machine 7. Like all other secrets, users will be able to relatively easily figure out the proxy password if they can modify a workflow file running on a self-hosted machine

View File

@@ -8,7 +8,7 @@
run-actions run scripts using a platform specific shell: run-actions run scripts using a platform specific shell:
`bash -eo pipefail` on non-windows, and `cmd.exe /c /d /s` on windows `bash -eo pipefail` on non-windows, and `cmd.exe /c /d /s` on windows
The `shell` option overwrites this to allow different flags or completely different shells/interpreters The `shell` option overrides this to allow different flags or completely different shells/interpreters
A small example is: A small example is:
```yml ```yml

View File

@@ -5,7 +5,7 @@
**Status**: Accepted **Status**: Accepted
## Context ## Context
First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most comment `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder. First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most common `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder.
There are serval different ways to get the hash `key` input for `actions/cache` action. There are serval different ways to get the hash `key` input for `actions/cache` action.
@@ -38,7 +38,7 @@ There are serval different ways to get the hash `key` input for `actions/cache`
`hashFiles()` will only support hashing files under the `$GITHUB_WORKSPACE` since the expression evaluated on the runner, if customer use job container or container action, the runner won't have access to file system inside the container. `hashFiles()` will only support hashing files under the `$GITHUB_WORKSPACE` since the expression evaluated on the runner, if customer use job container or container action, the runner won't have access to file system inside the container.
`hashFiles()` will only take 1 parameters: `hashFiles()` will only take 1 parameters:
- `hashFiles('**/package-lock.json')` // Search files under $GITHUB_WORKSPACE and calculate a hash for them - `hashFiles('**/package-lock.json')` // Search files under `$GITHUB_WORKSPACE` and calculate a hash for them
**Question: Do we need to support more than one match patterns?** **Question: Do we need to support more than one match patterns?**
Ex: `hashFiles('**/package-lock.json', '!toolkit/core/package-lock.json', '!toolkit/io/package-lock.json')` Ex: `hashFiles('**/package-lock.json', '!toolkit/core/package-lock.json', '!toolkit/io/package-lock.json')`
@@ -52,7 +52,7 @@ This will help customer has better experience with the `actions/cache` action's
key: ${{hashFiles('**/package-lock.json')}}-${{github.ref}}-${{runner.os}} key: ${{hashFiles('**/package-lock.json')}}-${{github.ref}}-${{runner.os}}
``` ```
For search pattern, we will use basic globbing (`*` `?` and `[]`) and globstar (`**`). For search pattern, we will use basic globbing (`*`, `?`, and `[]`) and globstar (`**`).
Additional pattern details: Additional pattern details:
- Root relative paths with `github.workspace` (the main repo) - Root relative paths with `github.workspace` (the main repo)
@@ -68,4 +68,4 @@ Hashing logic:
5. Use SHA256 to hash all stored files' hash results to get the final 64 chars hash result. 5. Use SHA256 to hash all stored files' hash results to get the final 64 chars hash result.
**Question: Should we include the folder structure info into the hash?** **Question: Should we include the folder structure info into the hash?**
Answer: No Answer: No

View File

@@ -6,10 +6,10 @@
## Context ## Context
In addition to action's regular execution, action author may wants their action has a chance to participate in: In addition to action's regular execution, action author may wants their action to have a chance to participate in:
- Job initialize - Job initialization
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the begin of the job. My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the beginning of the job.
- Job cleanup - Job cleanup
My Action will dirty local workspace or machine environment during execution, we need to cleanup these changes at the end of the job. My Action will dirty local workspace or machine environment during execution, we need to cleanup these changes at the end of the job.
Ex: `actions/checkout@v2` will write `github.token` into local `.git/config` during execution, it has post job cleanup defined to undo the changes. Ex: `actions/checkout@v2` will write `github.token` into local `.git/config` during execution, it has post job cleanup defined to undo the changes.
@@ -46,12 +46,12 @@ Container Action Example:
post-if: 'success()' // Optional post-if: 'success()' // Optional
``` ```
Both `pre` and `post` will has default `pre-if/post-if` sets to `always()`. Both `pre` and `post` will have default `pre-if/post-if` set to `always()`.
Setting `pre` to `always()` will make sure no matter what condition evaluate result the `main` gets at runtime, the `pre` has always run already. Setting `pre` to `always()` will make sure no matter what condition evaluate result the `main` gets at runtime, the `pre` has always run already.
`pre` executes in order of how the steps are defined. `pre` executes in order of how the steps are defined.
`pre` will always be added to job steps list during job setup. `pre` will always be added to job steps list during job setup.
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checkout during job initialize. > Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checked-out during job initialization.
> We can't use GitHub api to download the repository since there is a about 3 mins delay between `git push` and the new commit available to download using GitHub api. > We can't use GitHub api to download the repository since there is about a 3 minute delay between `git push` and the new commit available to download using GitHub api.
`post` will be pushed into a `poststeps` stack lazily when the action's `pre` or `main` execution passed `if` condition check and about to run, you can't have an action that only contains a `post`, we will pop and run each `post` after all `pre` and `main` finished. `post` will be pushed into a `poststeps` stack lazily when the action's `pre` or `main` execution passed `if` condition check and about to run, you can't have an action that only contains a `post`, we will pop and run each `post` after all `pre` and `main` finished.
> Currently `post` works for both repository action (`org/repo@v1`) and local action (`./my-action`) > Currently `post` works for both repository action (`org/repo@v1`) and local action (`./my-action`)
@@ -60,7 +60,7 @@ Valid action:
- only has `main` - only has `main`
- has `pre` and `main` - has `pre` and `main`
- has `main` and `post` - has `main` and `post`
- has `pre`, `main` and `post` - has `pre`, `main`, and `post`
Invalid action: Invalid action:
- only has `pre` - only has `pre`

View File

@@ -13,13 +13,13 @@ This is another version of [ADR275](https://github.com/actions/runner/pull/275)
## Decision ## Decision
This ADR proposes that we add a `--labels` option to `config`, which could be used to add custom additional labels to the configured runner. This ADR proposes that we add a `--labels` option to the `config`, which could be used to add custom additional labels to the configured runner.
For example, to add a single extra label the operator could run: For example, to add a single additional label the operator could run:
```bash ```bash
./config.sh --labels mylabel ./config.sh --labels mylabel
``` ```
> Note: the current runner command line parsing and envvar override algorithm only supports a single argument (key). > Note: the current runner command line parsing and envvar override algorithm only support a single argument (key).
This would add the label `mylabel` to the runner, and enable users to select the runner in their workflow using this label: This would add the label `mylabel` to the runner, and enable users to select the runner in their workflow using this label:
```yaml ```yaml
@@ -39,17 +39,17 @@ runs-on: [self-hosted, mylabel, anotherlabel]
It would not be possible to remove labels from an existing runner using `config.sh`, instead labels would have to be removed using the GitHub UI. It would not be possible to remove labels from an existing runner using `config.sh`, instead labels would have to be removed using the GitHub UI.
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commans in unattended config label names. Alternatively we could choose to escape commans but it's a nice to have. The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commas in unattended config label names. Alternatively, we could choose to escape commas but it's a nice to have.
## Replace ## Replace
If an existing runner exists and the option to replace is chosen (interactively of via unattend as in this scenario), then the labels will be replaced / overwritten (not merged). If an existing runner exists and the option to replace is chosen (interactively or via unattended as in this scenario), then the labels will be replaced/overwritten (not merged).
## Overriding built-in labels ## Overriding built-in labels
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org / runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future. Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org/runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
We will also not make other restrictions such as limiting explicitly adding os / arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future proofing / compat. We will also not make other restrictions such as limiting explicitly adding os/arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future-proofing / compatibility.
## Consequences ## Consequences

View File

@@ -8,17 +8,17 @@
Customers want to be able to compose actions from actions (ex: https://github.com/actions/runner/issues/438) 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. An important step towards meeting this goal is to build functionality for actions where users can simply execute any number of steps.
### Guiding Principles ### 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. 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 treat it as one logical step for the consumer.
A composite action is treated as **one** individual job step (this is known as encapsulation). A composite action is treated as **one** individual job step (this is known as encapsulation).
## Decision ## 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). **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 their parents' input variables and nested steps can overwrite the input variables).
### Composite Run Steps Features ### Composite Run Steps Features
This feature supports at the top action level: This feature supports at the top action level:
@@ -92,7 +92,7 @@ We will not support "defaults" in a composite action.
### Shell and Working-directory ### 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. The shell attribute is **required** for each run step because the action author does not know what the workflow author is using for the operating system so we need to explicitly prevent unknown behavior by making sure that each run step has an explicit shell **set by the action author.** On the other hand, `working-directory` is optional. 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 each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. The shell attribute is **required** for each run step because the action author does not know what the workflow author is using for the operating system so we need to explicitly prevent unknown behavior by making sure that each run step has an explicit shell **set by the action author.** On the other hand, `working-directory` is optional. Moreover, the composite action author can map in values from the `inputs` for its `shell` and `working-directory` attributes at the step level for an action.
For example, For example,
@@ -218,9 +218,9 @@ Example Output:
random-number 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`). 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's action output(s) are only viewable 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. 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 that we don't want to require the workflow author to know the internal workings of the composite action.
### Context ### Context
@@ -237,9 +237,9 @@ In the Composite Action, you'll only be able to use `::set-env::` to set environ
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. 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 ### If-Condition
** If and needs conditions will not be supported in the composite run steps feature. It will be supported later on in a new feature. ** ** `If` and `needs` conditions will not be supported in the composite run steps feature. It will be supported later on in a new feature. **
Old reasoning: Old reasoning:
@@ -248,7 +248,7 @@ Example `workflow.yml`:
```yaml ```yaml
steps: steps:
- run: exit 1 - run: exit 1
- uses: user/composite@v1 # <--- this will run, as it's marked as always runing - uses: user/composite@v1 # <--- this will run, as it's marked as always running
if: always() if: always()
``` ```
@@ -269,15 +269,15 @@ runs:
shell: bash shell: bash
``` ```
**We will not support "if Condition" in a composite action for now. This functionality will be focused on in a future ADR.** **We will not support "if-condition" in a composite action for now. This functionality will be focused on in a future ADR.**
See the paragraph below for a rudimentary approach (thank you to @cybojenix for the idea, example, and explanation for this approach): 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()`. 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.** **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. 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** have 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. 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.
@@ -314,13 +314,13 @@ runs:
**We will not support "timeout-minutes" in a composite action for now. This functionality will be focused on in a future ADR.** **We will not support "timeout-minutes" in a composite action for now. This functionality will be focused on in a future ADR.**
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. 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 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). If the time taken for any of the steps in combination or individually exceeds 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 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.
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. 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 affect other steps.
[Usage limits still apply](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions?query=if%28%29#usage-limits) [Usage limits still apply](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions?query=if%28%29#usage-limits)
@@ -361,7 +361,7 @@ For the composite action steps, it follows the same logic as above. In this exam
### Visualizing Composite Action in the GitHub Actions UI ### 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. 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) Here is a visual representation of the [first example](#Steps)
```yaml ```yaml
| composite_action_node | | composite_action_node |

View File

@@ -0,0 +1,92 @@
**Date**: 2021-06-10
**Status**: Accepted
## Context
We released [composite run steps](https://github.com/actions/runner/pull/554) last year which started our journey of reusing steps across different workflow files. To continue that journey, we want to expand composite run steps into composite actions.
We want to support the `uses` steps from workflows in composite actions, including:
- Container actions
- Javascript actions
- Other Composite actions (up to a limit of course!)
- The pre and post steps these actions can generate
## Guiding Principles
- Composite Actions should function as a single step or action, no matter how many steps it is composed of or how many levels of recursion it has
- In the future we may add a configurable option to make this no longer the case
- A workflow author should not need to understand the inner workings of a composite action in order to use it
- Composite actions should leverage inputs to get values they need, they will not have full access to the `context` objects. The secrets context will **not** be available to composite actions, users will need to pass these values in as an input.
- Other Actions should **just work** inside a composite action, without any code changes
## Decisions
### Composite Recursion Limit
- We will start with supporting a recursion limit of `10` composite actions deep
- We are free to bump this limit in the future, the code will be written to just require updating a variable. If the graph evaluates beyond the recursion limit, the job will fail in the pre-job phase (The `Set up job` step).
- A composite actions interface is its inputs and outputs, nothing else is carried over when invoking recursively.
### Pre/Post Steps in nested Actions
- We do not plan on adding the ability to configure a customizable pre or post step for composite actions at this time. However, we will execute the pre and post steps of any actions referenced in a composite action.
- Composite actions will generate a single pre-step and post-step for the entire composite action, even if there are multiple pre-steps and post-steps in the referenced actions.
- These steps will execute following the same ordering rules we have today, first to run has their pre step run first and their post step run last.
- For example, if you had a composite action with two pre steps and two posts steps:
```
- uses: action1
- uses: composite1
- uses: action2
```
The order of execution would be:
```
- prestep-action1
- prestep-composite1
- prestep-composite1-first-action-referenced
- prestep-composite1-second-action-referenced
- prestep-action2
- the job steps
- poststep-action2
- poststep-composite1
- poststep-composite1-the-second-action-referenced
- poststep-composite1-first-action-referenced
- poststep-action1
```
#### Set-state
- While the composite action has an individual combined pre/post action, the `set-state` command will not be shared.
- If the `set-state` command is used during a composite step, only the action that originally called `set-state` will have access to the env variable during the post run step.
- This prevents multiple actions that set the same state from interfering with the execution of another action's post step.
### Resolve Action Endpoint changes
- The resolve actions endpoint will now validate policy to ensure that the given workflow run has access to download that action.
- Older GHES/GHAE customers with newer runners will be locked out of composite uses steps until they upgrade their instance.
### Local actions
- Local actions will expand the tree, perform policy checks, and download actions Just in Time when the step is running.
- Like current local actions, we will not support presteps. If an action is running local, by the time we know that, the time to run presteps have already passed.
### If, continue-on-error, timeout-minutes - Not being considered at this time
- `if`, `continue-on-error`, `timeout-minutes` could be supported in composite run/uses steps. These values were not originally supported in our composite run steps implementation.
- Browsing the community forums and runner repo, there hasn't been a lot of noise asking for these features, so we will hold off on them.
- These values passed as input into the composite action will **not** be carried over as input into the individual steps the composite action runs.
### Defaults - Not being considered at this time
- In actions, we have the idea of [defaults](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#defaultsrun) , which allow you to specify a shell and working directory in one location, rather then on each step.
- However, `shell` is currently required in composite run steps
- In regular run steps, it is optional, and defaults to a different value based on the OS.
- We want to prioritize the right experience for the consumer, and make the action author continue to explicitly set these values. We can consider improving this experience in the future.
## Consequences
- Workflows are now more reusable across multiple workflow files
- Composite actions implement most of the existing workflow run steps, with room to expand these in the future
- Feature flags will control this rollout

View File

@@ -0,0 +1,71 @@
# ADR 1438: Support Conditionals In Composite Actions
**Date**: 2021-10-13
**Status**: Accepted
## Context
We recently shipped composite actions, which allows you to reuse individual steps inside an action.
However, one of the [most requested features](https://github.com/actions/runner/issues/834) has been a way to support the `if` keyword.
### Goals
- We want to keep consistent with current behavior
- We want to support conditionals via the `if` keyword
- Our built in functions like `success` should be implementable without calling them, for example you can do `job.status == success` rather then `success()` currently.
### How does composite currently work?
Currently, we have limited conditional support in composite actions for `pre` and `post` steps.
These are based on the `job status`, and support keywords like `always()`, `failed()`, `success()` and `cancelled()`.
However, generic or main steps do **not** support conditionals.
By default, in a regular workflow, a step runs on the `success()` condition. Which looks at the **job** **status**, sees if it is successful and runs.
By default, in a composite action, main steps run until a single step fails in that composite action, then the composite action is halted early. It does **not** care about the job status.
Pre, and post steps in composite actions use the job status to determine if they should run.
### How do we go forward?
Well, if we think about what composite actions are currently doing when invoking main steps, they are checking if the current composite action is successful.
Lets formalize that concept into a "real" idea.
- We will add an `action_status` field to the github context to mimic the [job's context status](https://docs.github.com/en/actions/learn-github-actions/contexts#job-context).
- We have an existing concept that does this `action_path` which is only set for composite actions on the github context.
- In a composite action during a main step, the `success()` function will check if `action_status == success`, rather then `job_status == success`. Failure will work the same way.
- Pre and post steps in composite actions will not change, they will continue to check the job status.
### Nested Scenario
For nested composite actions, we will follow the existing behavior, you only care about your current composite action, not any parents.
For example, lets imagine a scenario with a simple nested composite action
```
- Job
- Regular Step
- Composite Action
- runs: exit 1
- if: always()
uses: A child composite action
- if: success()
runs: echo "this should print"
- runs: echo "this should also print"
- if: success()
runs: echo "this will not print as the current composite action has failed already"
```
The child composite actions steps should run in this example, the child composite action has not yet failed, so it should run all steps until a step fails. This is consistent with how a composite action currently works in production if the main job fails but a composite action is invoked with `if:always()` or `if: failure()`
### Other options explored
We could add the `current_step_status` to the job context rather then `__status` to the steps context, however this comes with two major downsides:
- We need to support the field for every type of step, because its non trivial to remove a field from the job context once it has been added (its readonly)
- For all actions besides composite it would only every be `success`
- Its weird to have a `current_step` value on the job context
- We also explored a `__status` on the steps context.
- The `__` is required to prevent us from colliding with a step with id: status
- This felt wrong because the naming was not smooth, and did not fit into current conventions.
### Consequences
- github context has a new field for the status of the current composite action.
- We support conditional's in composite actions
- We keep the existing behavior for all users, but allow them to expand that functionality.

View File

@@ -11,7 +11,7 @@ export RUNNER_CFG_PAT=yourPAT
## Create running as a service ## Create running as a service
**Scenario**: Run on a machine or VM (not container) which automates: **Scenario**: Run on a machine or VM ([not container](#why-cant-i-use-a-container)) which automates:
- Resolving latest released runner - Resolving latest released runner
- Download and extract latest - Download and extract latest
@@ -21,14 +21,35 @@ export RUNNER_CFG_PAT=yourPAT
:point_right: [Sample script here](../scripts/create-latest-svc.sh) :point_left: :point_right: [Sample script here](../scripts/create-latest-svc.sh) :point_left:
Run as a one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) Run as a one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
```bash ```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo
``` ```
## Uninstall running as service You can call the script with additional arguments:
```bash
# Usage:
# export RUNNER_CFG_PAT=<yourPAT>
# ./create-latest-svc -s scope -g [ghe_domain] -n [name] -u [user] -l [labels]
# -s required scope: repo (:owner/:repo) or org (:organization)
# -g optional ghe_hostname: the fully qualified domain name of your GitHub Enterprise Server deployment
# -n optional name of the runner, defaults to hostname
# -u optional user svc will run as, defaults to current
# -l optional list of labels (split by comma) applied on the runner"
```
**Scenario**: Run on a machine or VM (not container) which automates: Use `--` to pass any number of optional named parameters:
```
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/create-latest-svc.sh | bash -s -- -s myorg/myrepo -n myname -l label1,label2
```
### Why can't I use a container?
The runner is installed as a service using `systemd` and `systemctl`. Docker does not support `systemd` for service configuration on a container.
## Uninstall running as service
**Scenario**: Run on a machine or VM ([not container](#why-cant-i-use-a-container)) which automates:
- Stops and uninstalls the systemd (linux) or Launchd (osx) service - Stops and uninstalls the systemd (linux) or Launchd (osx) service
- Acquires a removal token - Acquires a removal token
@@ -36,9 +57,9 @@ curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/create
:point_right: [Sample script here](../scripts/remove-svc.sh) :point_left: :point_right: [Sample script here](../scripts/remove-svc.sh) :point_left:
Repo level one liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) Repo level one liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
```bash ```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/remove-svc.sh | bash -s yourorg/yourrepo curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/remove-svc.sh | bash -s yourorg/yourrepo
``` ```
### Delete an offline runner ### Delete an offline runner
@@ -53,5 +74,5 @@ curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/remove
Repo level one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) and replace runnername Repo level one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) and replace runnername
```bash ```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/delete.sh | bash -s yourorg/yourrepo runnername curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/delete.sh | bash -s yourorg/yourrepo runnername
``` ```

View File

@@ -18,16 +18,16 @@ Make sure the runner has access to actions service for GitHub.com or GitHub Ente
- DNS lookup for api.github.com or myGHES.com using dotnet - DNS lookup for api.github.com or myGHES.com using dotnet
- Ping api.github.com or myGHES.com using dotnet - Ping api.github.com or myGHES.com using dotnet
- Make HTTP GET to https://api.github.com or https://myGHES.com/api/v3 using dotnet, check response headers contains `X-GitHub-Request-Id` - Make HTTP GET to https://api.github.com or https://myGHES.com/api/v3 using dotnet, check response headers contains `X-GitHub-Request-Id`
--- ---
- DNS lookup for vstoken.actions.githubusercontent.com using dotnet - DNS lookup for vstoken.actions.githubusercontent.com using dotnet
- Ping vstoken.actions.githubusercontent.com using dotnet - Ping vstoken.actions.githubusercontent.com using dotnet
- Make HTTP GET to https://vstoken.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/vstoken/_apis/health using dotnet, check response headers contains `x-vss-e2eid` - Make HTTP GET to https://vstoken.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/vstoken/_apis/health using dotnet, check response headers contains `x-vss-e2eid`
--- ---
- DNS lookup for pipelines.actions.githubusercontent.com using dotnet - DNS lookup for pipelines.actions.githubusercontent.com using dotnet
- Ping pipelines.actions.githubusercontent.com using dotnet - Ping pipelines.actions.githubusercontent.com using dotnet
- Make HTTP GET to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid` - Make HTTP GET to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid`
- Make HTTP POST to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid` - Make HTTP POST to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid`
## How to fix the issue? ## How to fix the issue?
@@ -42,4 +42,4 @@ Make sure the runner has access to actions service for GitHub.com or GitHub Ente
## Still not working? ## Still not working?
Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue. Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue.

View File

@@ -31,4 +31,4 @@ The test also set environment variable `GIT_TRACE=1` and `GIT_CURL_VERBOSE=1` be
## Still not working? ## Still not working?
Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue. Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue.

View File

@@ -13,7 +13,7 @@ Even the runner is configured to GitHub Enterprise Server, the runner can still
- DNS lookup for api.github.com using dotnet - DNS lookup for api.github.com using dotnet
- Ping api.github.com using dotnet - Ping api.github.com using dotnet
- Make HTTP GET to https://api.github.com using dotnet, check response headers contains `X-GitHub-Request-Id` - Make HTTP GET to https://api.github.com using dotnet, check response headers contains `X-GitHub-Request-Id`
## How to fix the issue? ## How to fix the issue?
@@ -23,4 +23,4 @@ Even the runner is configured to GitHub Enterprise Server, the runner can still
## Still not working? ## Still not working?
Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue. Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue.

View File

@@ -2,17 +2,19 @@
### Common things that can cause the runner to not working properly ### Common things that can cause the runner to not working properly
- Bug in the runner or the dotnet framework that causes actions runner can't make Http request in a certain network environment. - A bug in the runner or the dotnet framework that causes the actions runner to be unable to make Http requests in a certain network environment.
- Proxy/Firewall block certain HTTP method, like it block all POST and PUT calls which the runner will use to upload logs. - A Proxy or Firewall may block certain HTTP method, such as blocking all POST and PUT calls which the runner will use to upload logs.
- Proxy/Firewall only allows requests with certain user-agent to pass through and the actions runner user-agent is not in the allow list. - A Proxy or Firewall may only allows requests with certain user-agent to pass through and the actions runner user-agent is not in the allow list.
- Proxy try to decrypt and exam HTTPS traffic for security purpose but cause the actions-runner to fail to finish SSL handshake due to the lack of trusting proxy's CA. - A Proxy try to decrypt and exam HTTPS traffic for security purpose but cause the actions-runner to fail to finish SSL handshake due to the lack of trusting proxy's CA.
- Proxy try to modify the HTTPS request (like add or change some http headers) and causes the request become incompatible with the Actions Service (ASP.NetCore), Ex: [Nginx](https://github.com/dotnet/aspnetcore/issues/17081) - The SSL handshake may fail if the client and server do not support the same TLS version, or the same cipher suites.
- Firewall rules that block action runner from accessing certain hosts, ex: `*.github.com`, `*.actions.githubusercontent.com`, etc. - A Proxy may try to modify the HTTPS request (like add or change some http headers) and causes the request become incompatible with the Actions Service (ASP.NetCore), Ex: [Nginx](https://github.com/dotnet/aspnetcore/issues/17081)
- Firewall rules that block action runner from accessing certain hosts, ex: `*.github.com`, `*.actions.githubusercontent.com`, etc
### Identify and solve these problems ### Identify and solve these problems
@@ -23,10 +25,38 @@ Use a 3rd party tool to make the same requests as the runner did would be a good
- Use `nslookup` to check DNS - Use `nslookup` to check DNS
- Use `ping` to check Ping - Use `ping` to check Ping
- Use `traceroute`, `tracepath`, or `tracert` to check the network route between the runner and the Actions service - Use `traceroute`, `tracepath`, or `tracert` to check the network route between the runner and the Actions service
- Use `curl -v` to check the network stack, good for verifying default certificate/proxy settings. - Use `curl -v` to check the network stack, good for verifying default certificate/proxy settings.
- Use `Invoke-WebRequest` from `pwsh` (`PowerShell Core`) to check the dotnet network stack, good for verifying bugs in the dotnet framework. - Use `Invoke-WebRequest` from `pwsh` (`PowerShell Core`) to check the dotnet network stack, good for verifying bugs in the dotnet framework.
If the 3rd party tool is also experiencing the same error as the runner does, then you might want to contact your network administrator for help. If the 3rd party tool is also experiencing the same error as the runner does, then you might want to contact your network administrator for help.
Otherwise, contact GitHub customer support or log an issue at https://github.com/actions/runner Otherwise, contact GitHub customer support or log an issue at https://github.com/actions/runner
### Troubleshooting: Why can't I configure a runner?
If you are having trouble connecting, try these steps:
1. Validate you can reach our endpoints from your web browser. If not, double check your local network connection
- For hosted Github:
- https://api.github.com/
- https://vstoken.actions.githubusercontent.com/_apis/health
- https://pipelines.actions.githubusercontent.com/_apis/health
- For GHES/GHAE
- https://myGHES.com/_services/vstoken/_apis/health
- https://myGHES.com/_services/pipelines/_apis/health
- https://myGHES.com/api/v3
2. Validate you can reach those endpoints in powershell core
- The runner runs on .net core, lets validate the local settings for that stack
- Open up `pwsh`
- Run the command using the urls above `Invoke-WebRequest {url}`
3. If not, get a packet trace using a tool like wireshark and start looking at the TLS handshake.
- If you see a Client Hello followed by a Server RST:
- You may need to configure your TLS settings to use the correct version
- You should support TLS version 1.2 or later
- You may need to configure your TLS settings to have up to date cipher suites, this may be solved by system updates and patches.
- Most notably, on windows server 2012 make sure [the tls cipher suite update](https://support.microsoft.com/en-us/topic/update-adds-new-tls-cipher-suites-and-changes-cipher-suite-priorities-in-windows-8-1-and-windows-server-2012-r2-8e395e43-c8ef-27d8-b60c-0fc57d526d94) is installed
- Your firewall, proxy or network configuration may be blocking the connection
- You will want to reach out to whoever is in charge of your network with these pcap files to further troubleshoot
- If you see a failure later in the handshake:
- Try the fix in the [SSLCert Fix](./sslcert.md)

View File

@@ -27,4 +27,4 @@ All javascript base Actions will get executed by the built-in `node` at `<runner
## Still not working? ## Still not working?
Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue. Contact GitHub customer service or log an issue at https://github.com/actions/runner if you think it's a runner issue.

View File

@@ -12,7 +12,7 @@ As long as your certificate is generated properly, most of the issues should be
> !!! DO NOT SKIP SSL CERT VALIDATION !!! > !!! DO NOT SKIP SSL CERT VALIDATION !!!
> !!! IT IS A BAD SECURITY PRACTICE !!! > !!! IT IS A BAD SECURITY PRACTICE !!!
### Download SSL certificate chain ### Download SSL certificate chain
Depends on how your SSL server certificate gets configured, you might need to download the whole certificate chain from a machine that has trusted the SSL certificate's CA. Depends on how your SSL server certificate gets configured, you might need to download the whole certificate chain from a machine that has trusted the SSL certificate's CA.
@@ -28,7 +28,7 @@ The actions runner is a dotnet core application which will follow how dotnet loa
You can get full details documentation at [here](https://docs.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography#x509store) You can get full details documentation at [here](https://docs.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography#x509store)
In short: In short:
- Windows: Load from Windows certificate store. - Windows: Load from Windows certificate store.
- Linux: Load from OpenSSL CA cert bundle. - Linux: Load from OpenSSL CA cert bundle.
- macOS: Load from macOS KeyChain. - macOS: Load from macOS KeyChain.
@@ -43,13 +43,13 @@ To let the runner trusts your CA certificate, you will need to:
1. RedHat: https://www.redhat.com/sysadmin/ca-certificates-cli 1. RedHat: https://www.redhat.com/sysadmin/ca-certificates-cli
2. Ubuntu: http://manpages.ubuntu.com/manpages/focal/man8/update-ca-certificates.8.html 2. Ubuntu: http://manpages.ubuntu.com/manpages/focal/man8/update-ca-certificates.8.html
3. Google search: "trust ca certificate on [linux distribution]" 3. Google search: "trust ca certificate on [linux distribution]"
4. If all approaches failed, set environment variable `SSL_CERT_FILE` to the CA bundle `.pem` file we get. 4. If all approaches failed, set environment variable `SSL_CERT_FILE` to the CA bundle `.pem` file we get.
> To verity cert gets installed properly on Linux, you can try use `curl -v https://sitewithsslissue.com` and `pwsh -Command \"Invoke-WebRequest -Uri https://sitewithsslissue.com\"` > To verify cert gets installed properly on Linux, you can try use `curl -v https://sitewithsslissue.com` and `pwsh -Command \"Invoke-WebRequest -Uri https://sitewithsslissue.com\"`
### Trust CA certificate for Git CLI ### Trust CA certificate for Git CLI
Git uses various CA bundle file depends on your operation system. Git uses various CA bundle file depends on your operation system.
- Git packaged the CA bundle file within the Git installation on Windows - Git packaged the CA bundle file within the Git installation on Windows
- Git use OpenSSL certificate CA bundle file on Linux and macOS - Git use OpenSSL certificate CA bundle file on Linux and macOS
You can check where Git check CA file by running: You can check where Git check CA file by running:

View File

@@ -12,20 +12,43 @@ Issues in this repository should be for the runner application. Note that the V
## Enhancements and Feature Requests ## Enhancements and Feature Requests
We ask that before significant effort is put into code changes, that we have agreement on taking the change before time is invested in code changes. We ask that before significant effort is put into code changes, that we have agreement on taking the change before time is invested in code changes.
1. Create a feature request. Once agreed we will take the enhancement 1. Create a feature request. Once agreed we will take the enhancement
2. Create an ADR to agree on the details of the change. 2. Create an ADR to agree on the details of the change.
An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](adrs/README.md) An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](adrs/README.md)
## Development Life Cycle ## Required Dev Dependencies
### Required Dev Dependencies
![Win](res/win_sm.png) ![*nix](res/linux_sm.png) Git for Windows and Linux [Install Here](https://git-scm.com/downloads) (needed for dev sh script) ![Win](res/win_sm.png) ![*nix](res/linux_sm.png) Git for Windows and Linux [Install Here](https://git-scm.com/downloads) (needed for dev sh script)
### To Build, Test, Layout ## Quickstart: Run a job from a real repository
If you just want to get from building the sourcecode to using it to execute an action, you will need:
- The url of your repository
- A runner registration token. You can find it at `https://github.com/{your-repo}/settings/actions/runners/new`
```bash
git clone https://github.com/actions/runner
cd runner/src
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
cd ../_layout
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB # accept default name, labels and work folder
./run.(sh/cmd)
```
If you trigger a job now, you can see the runner execute it.
Tip: Make sure your job can run on this runner. The easiest way is to set `runs-on: self-hosted` in the workflow file.
## Development Life Cycle
If you're using VS Code, you can follow [these](contribute/vscode.md) steps instead.
### To Build, Test, Layout
Navigate to the `src` directory and run the following command: Navigate to the `src` directory and run the following command:
@@ -39,7 +62,7 @@ Navigate to the `src` directory and run the following command:
* `build` (`b`): Build everything and update runner layout folder * `build` (`b`): Build everything and update runner layout folder
* `test` (`t`): Build runner binaries and run unit tests * `test` (`t`): Build runner binaries and run unit tests
Sample developer flow: ### Sample developer flow:
```bash ```bash
git clone https://github.com/actions/runner git clone https://github.com/actions/runner
@@ -51,25 +74,81 @@ 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: Let's break that down.
### Clone repository:
```bash
git clone https://github.com/actions/runner
cd runner
```
If you want to push your changes to a remote, it is recommended you fork the repository and use that fork as your origin instead of `https://github.com/actions/runner`.
### Build Layout:
This command will build all projects, then copies them and other dependencies into a folder called `_layout`. The binaries in this folder are then used for running, debugging the runner.
```bash
cd ./src # execute the script from this folder
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
```
If you make code changes after this point, use the argument `build` to build your code in the `src` folder to keep your `_layout` folder up to date.
```bash
cd ./src
./dev.(sh/cmd) build # {root}/_layout will get updated
```
### Test Layout:
This command runs the suite of unit tests in the project
```bash
cd ./src
./dev.(sh/cmd) test # run all unit tests before git commit/push
```
### Configure Runner:
If you want to manually test your runner and run actions from a real repository, you'll have to configure it before running it.
```bash
cd runner/_layout
./config.(sh/cmd) # configure your custom runner
```
You will need your the name of your repository and a runner registration token.
Check [Quickstart](##Quickstart:-Run-a-job-from-a-real-repository) if you don't know how to get this token.
These can also be passed down as arguments to `config.(sh/cmd)`:
```bash
cd runner/_layout
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB
```
### Run Runner
All that's left to do is to start the runner:
```bash
cd runner/_layout
./run.(sh/cmd) # run your custom runner
```
### View logs:
```bash ```bash
cd runner/_layout/_diag cd runner/_layout/_diag
ls ls
cat (Runner/Worker)_TIMESTAMP.log # view your log file cat (Runner/Worker)_TIMESTAMP.log # view your log file
``` ```
Run Runner: ## Editors
```bash
cd runner/_layout
./run.sh # run your custom runner
```
### Editors
[Using Visual Studio Code](https://code.visualstudio.com/) [Using Visual Studio Code](https://code.visualstudio.com/)
[Using Visual Studio](https://code.visualstudio.com/docs) [Using Visual Studio](https://code.visualstudio.com/docs)
### Styling ## Styling
We use the .NET Foundation and CoreCLR style guidelines [located here]( We use the .NET Foundation and CoreCLR style guidelines [located here](
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md) https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)

52
docs/contribute/vscode.md Normal file
View File

@@ -0,0 +1,52 @@
# Development Life Cycle using VS Code:
These examples use VS Code, but the idea should be similar across all IDEs as long as you attach to the same processes in the right folder.
## Configure
To successfully start the runner, you need to register it using a repository and a runner registration token.
Run `Configure` first to build the source code and set up the runner in `_layout`.
Once it's done creating `_layout`, it asks for the url of your repository and your token in the terminal.
Check [Quickstart](../contribute.md#quickstart-run-a-job-from-a-real-repository) if you don't know how to get this token.
## Debugging
Debugging the full lifecycle of a job can be tricky, because there are multiple processes involved.
All the configs below can be found in `.vscode/launch.json`.
## Debug the Listener
```json
{
"name": "Run [build]",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build runner layout", // use the config called "Run" to launch without rebuild
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run" // run without args to print usage
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
}
```
If you launch `Run` or `Run [build]`, it starts a process called `Runner.Listener`.
This process will receive any job queued on this repository if the job runs on matching labels (e.g `runs-on: self-hosted`).
Once a job is received, a `Runner.Listener` starts a new process of `Runner.Worker`.
Since this is a diferent process, you can't use the same debugger session debug it.
Instead, a parallel debugging session has to be started, using a different launch config.
Luckily, VS Code supports multiple parallel debugging sessions.
## Debug the Worker
Because the worker process is usually started by the listener instead of an IDE, debugging it from start to finish can be tricky.
For this reason, `Runner.Worker` can be configured to wait for a debugger to be attached before it begins any actual work.
Set the environment variable `GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER` to `true` or `1` to enable this wait.
All worker processes now will wait 20 seconds before they start working on their task.
This gives enough time to attach a debugger by running `Debug Worker`.
If for some reason you have multiple workers running, run the launch config `Attach` instead.
Select `Runner.Worker` from the running processes when VS Code prompts for it.

View File

@@ -58,4 +58,4 @@ Authentication in a workflow run to github.com can be accomplished by using the
Hosted runner authentication differs from self-hosted authentication in that runners do not undergo a registration process, but instead, the hosted runners get the OAuth token directly by reading the `.credentials` file. The scope of this particular token is limited for a given workflow job execution, and the token is revoked as soon as the job is finished. Hosted runner authentication differs from self-hosted authentication in that runners do not undergo a registration process, but instead, the hosted runners get the OAuth token directly by reading the `.credentials` file. The scope of this particular token is limited for a given workflow job execution, and the token is revoked as soon as the job is finished.
![Hosted runner config and start](../res/hosted-config-start.png) ![Hosted runner config and start](../res/hosted-config-start.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -23,11 +23,11 @@ You might see something like this which indicate a dependency's missing.
./config.sh ./config.sh
libunwind.so.8 => not found libunwind.so.8 => not found
libunwind-x86_64.so.8 => not found libunwind-x86_64.so.8 => not found
Dependencies is missing for Dotnet Core 3.0 Dependencies is missing for Dotnet Core 6.0
Execute ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies. Execute ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.
``` ```
You can easily correct the problem by executing `./bin/installdependencies.sh`. You can easily correct the problem by executing `./bin/installdependencies.sh`.
The `installdependencies.sh` script should install all required dependencies on all supported Linux versions The `installdependencies.sh` script should install all required dependencies on all supported Linux versions
> Note: The `installdependencies.sh` script will try to use the default package management mechanism on your Linux flavor (ex. `yum`/`apt-get`/`apt`). > Note: The `installdependencies.sh` script will try to use the default package management mechanism on your Linux flavor (ex. `yum`/`apt-get`/`apt`).
### Full dependencies list ### Full dependencies list
@@ -35,15 +35,15 @@ The `installdependencies.sh` script should install all required dependencies on
Debian based OS (Debian, Ubuntu, Linux Mint) Debian based OS (Debian, Ubuntu, Linux Mint)
- liblttng-ust0 - liblttng-ust0
- libkrb5-3 - libkrb5-3
- zlib1g - zlib1g
- libssl1.1, libssl1.0.2 or libssl1.0.0 - libssl1.1, libssl1.0.2 or libssl1.0.0
- libicu63, libicu60, libicu57 or libicu55 - libicu63, libicu60, libicu57 or libicu55
Fedora based OS (Fedora, Red Hat Enterprise Linux, CentOS, Oracle Linux 7) Fedora based OS (Fedora, Red Hat Enterprise Linux, CentOS, Oracle Linux 7)
- lttng-ust - lttng-ust
- openssl-libs - openssl-libs
- krb5-libs - krb5-libs
- zlib - zlib
- libicu - libicu

View File

@@ -6,5 +6,11 @@
- macOS High Sierra (10.13) and later versions - macOS High Sierra (10.13) and later versions
## Apple Silicon M1
The runner is currently not supported on devices with an Apple M1 chip.
We are waiting for official .NET support. You can read more here about the [current state of support here](https://github.com/orgs/dotnet/projects/18#card-56812463).
Current .NET project board about M1 support:
https://github.com/orgs/dotnet/projects/18#card-56812463
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30) ## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)

View File

@@ -1,16 +1,22 @@
## Features ## Features
- Verify the Runner Hash during auto-upgrade before installing the new runner version (#967)
- Support download of runners from authenticated endpoints (#920) - Bump runtime to dotnet 6 (#1471)
- Enabled tty output in Docker Actions (#916) - Show service container logs on teardown (#1563)
- Added '--check' command to verify runner connectivity (#949)
## Bugs ## Bugs
- Fix usage of /dev/null and ping in run.sh (#968)
- Add masks for multiline secrets from ::add-mask:: (#1521)
- fix Log size and retention settings not work (#1507)
- Refactor SelfUpdater adding L0 tests. (#1564)
- Fix test failure: /bin/sleep on Macos 11 (Monterey) does not accept the suffix s. (#1472)
## Misc ## Misc
- Updated the copy for various runner messages (#972)
- Added the runner's OS to telemetry (#939) - Update dependency check for dotnet 6. (#1551)
- Various other telemetry improvements (#935) - Produce trimmed down runner packages. (#1556)
- Deleted extra background in github-praph.png, which is displayed in README.md (#1432)
## Windows x64 ## Windows x64
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows. We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
@@ -22,7 +28,7 @@ mkdir \actions-runner ; cd \actions-runner
# Download the latest runner package # Download the latest runner package
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip
# Extract the installer # Extract the installer
Add-Type -AssemblyName System.IO.Compression.FileSystem ; Add-Type -AssemblyName System.IO.Compression.FileSystem ;
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD") [System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
``` ```
@@ -48,7 +54,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
``` ```
## Linux arm64 (Pre-release) ## Linux arm64
``` bash ``` bash
# Create a folder # Create a folder
@@ -59,7 +65,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
``` ```
## Linux arm (Pre-release) ## Linux arm
``` bash ``` bash
# Create a folder # Create a folder
@@ -82,3 +88,21 @@ The SHA-256 checksums for the packages included in this build are shown below:
- actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-x64 --><LINUX_X64_SHA><!-- END SHA linux-x64 --> - actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-x64 --><LINUX_X64_SHA><!-- END SHA linux-x64 -->
- actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm64 --><LINUX_ARM64_SHA><!-- END SHA linux-arm64 --> - actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm64 --><LINUX_ARM64_SHA><!-- END SHA linux-arm64 -->
- actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm --><LINUX_ARM_SHA><!-- END SHA linux-arm --> - actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm --><LINUX_ARM_SHA><!-- END SHA linux-arm -->
- actions-runner-win-x64-<RUNNER_VERSION>-noexternals.zip <!-- BEGIN SHA win-x64_noexternals --><WIN_X64_SHA_NOEXTERNALS><!-- END SHA win-x64_noexternals -->
- actions-runner-osx-x64-<RUNNER_VERSION>-noexternals.tar.gz <!-- BEGIN SHA osx-x64_noexternals --><OSX_X64_SHA_NOEXTERNALS><!-- END SHA osx-x64_noexternals -->
- actions-runner-linux-x64-<RUNNER_VERSION>-noexternals.tar.gz <!-- BEGIN SHA linux-x64_noexternals --><LINUX_X64_SHA_NOEXTERNALS><!-- END SHA linux-x64_noexternals -->
- actions-runner-linux-arm64-<RUNNER_VERSION>-noexternals.tar.gz <!-- BEGIN SHA linux-arm64_noexternals --><LINUX_ARM64_SHA_NOEXTERNALS><!-- END SHA linux-arm64_noexternals -->
- actions-runner-linux-arm-<RUNNER_VERSION>-noexternals.tar.gz <!-- BEGIN SHA linux-arm_noexternals --><LINUX_ARM_SHA_NOEXTERNALS><!-- END SHA linux-arm_noexternals -->
- actions-runner-win-x64-<RUNNER_VERSION>-noruntime.zip <!-- BEGIN SHA win-x64_noruntime --><WIN_X64_SHA_NORUNTIME><!-- END SHA win-x64_noruntime -->
- actions-runner-osx-x64-<RUNNER_VERSION>-noruntime.tar.gz <!-- BEGIN SHA osx-x64_noruntime --><OSX_X64_SHA_NORUNTIME><!-- END SHA osx-x64_noruntime -->
- actions-runner-linux-x64-<RUNNER_VERSION>-noruntime.tar.gz <!-- BEGIN SHA linux-x64_noruntime --><LINUX_X64_SHA_NORUNTIME><!-- END SHA linux-x64_noruntime -->
- actions-runner-linux-arm64-<RUNNER_VERSION>-noruntime.tar.gz <!-- BEGIN SHA linux-arm64_noruntime --><LINUX_ARM64_SHA_NORUNTIME><!-- END SHA linux-arm64_noruntime -->
- actions-runner-linux-arm-<RUNNER_VERSION>-noruntime.tar.gz <!-- BEGIN SHA linux-arm_noruntime --><LINUX_ARM_SHA_NORUNTIME><!-- END SHA linux-arm_noruntime -->
- actions-runner-win-x64-<RUNNER_VERSION>-noruntime-noexternals.zip <!-- BEGIN SHA win-x64_noruntime_noexternals --><WIN_X64_SHA_NORUNTIME_NOEXTERNALS><!-- END SHA win-x64_noruntime_noexternals -->
- actions-runner-osx-x64-<RUNNER_VERSION>-noruntime-noexternals.tar.gz <!-- BEGIN SHA osx-x64_noruntime_noexternals --><OSX_X64_SHA_NORUNTIME_NOEXTERNALS><!-- END SHA osx-x64_noruntime_noexternals -->
- actions-runner-linux-x64-<RUNNER_VERSION>-noruntime-noexternals.tar.gz <!-- BEGIN SHA linux-x64_noruntime_noexternals --><LINUX_X64_SHA_NORUNTIME_NOEXTERNALS><!-- END SHA linux-x64_noruntime_noexternals -->
- actions-runner-linux-arm64-<RUNNER_VERSION>-noruntime-noexternals.tar.gz <!-- BEGIN SHA linux-arm64_noruntime_noexternals --><LINUX_ARM64_SHA_NORUNTIME_NOEXTERNALS><!-- END SHA linux-arm64_noruntime_noexternals -->
- actions-runner-linux-arm-<RUNNER_VERSION>-noruntime-noexternals.tar.gz <!-- BEGIN SHA linux-arm_noruntime_noexternals --><LINUX_ARM_SHA_NORUNTIME_NOEXTERNALS><!-- END SHA linux-arm_noruntime_noexternals -->

View File

@@ -1 +1 @@
<Update to ./src/runnerversion when creating release> 2.286.0

View File

@@ -1,4 +1,4 @@
# Sample scripts for self-hosted runners # Sample scripts for self-hosted runners
Here are some examples to work from if you'd like to automate your use of self-hosted runners. Here are some examples to work from if you'd like to automate your use of self-hosted runners.
See the docs [here](../docs/automate.md). See the docs [here](../docs/automate.md).

View File

@@ -2,36 +2,68 @@
set -e set -e
#
# Downloads latest releases (not pre-release) runner
# Configures as a service
#
# Examples:
# RUNNER_CFG_PAT=<yourPAT> ./create-latest-svc.sh myuser/myrepo my.ghe.deployment.net
# RUNNER_CFG_PAT=<yourPAT> ./create-latest-svc.sh myorg my.ghe.deployment.net
#
# Usage:
# export RUNNER_CFG_PAT=<yourPAT>
# ./create-latest-svc scope [ghe_domain] [name] [user] [labels]
#
# scope required repo (:owner/:repo) or org (:organization)
# ghe_domain optional the fully qualified domain name of your GitHub Enterprise Server deployment
# name optional defaults to hostname
# user optional user svc will run as. defaults to current
# labels optional list of labels (split by comma) applied on the runner
#
# Notes: # Notes:
# PATS over envvars are more secure # PATS over envvars are more secure
# Downloads latest runner release (not pre-release)
# Configures it as a service more secure
# Should be used on VMs and not containers # Should be used on VMs and not containers
# Works on OSX and Linux # Works on OSX and Linux
# Assumes x64 arch # Assumes x64 arch
# # See EXAMPLES below
runner_scope=${1} flags_found=false
ghe_hostname=${2}
runner_name=${3:-$(hostname)} while getopts 's:g:n:u:l:' opt; do
svc_user=${4:-$USER} flags_found=true
labels=${5}
case $opt in
s)
runner_scope=$OPTARG
;;
g)
ghe_hostname=$OPTARG
;;
n)
runner_name=$OPTARG
;;
u)
svc_user=$OPTARG
;;
l)
labels=$OPTARG
;;
*)
echo "
Runner Service Installer
Examples:
RUNNER_CFG_PAT=<yourPAT> ./create-latest-svc.sh myuser/myrepo my.ghe.deployment.net
RUNNER_CFG_PAT=<yourPAT> ./create-latest-svc.sh -s myorg -u user_name -l label1,label2
Usage:
export RUNNER_CFG_PAT=<yourPAT>
./create-latest-svc scope [ghe_domain] [name] [user] [labels]
-s required scope: repo (:owner/:repo) or org (:organization)
-g optional ghe_hostname: the fully qualified domain name of your GitHub Enterprise Server deployment
-n optional name of the runner, defaults to hostname
-u optional user svc will run as, defaults to current
-l optional list of labels (split by comma) applied on the runner"
exit 0
;;
esac
done
shift "$((OPTIND - 1))"
if ! "$flags_found"; then
runner_scope=${1}
ghe_hostname=${2}
runner_name=${3:-$(hostname)}
svc_user=${4:-$USER}
labels=${5}
fi
# apply defaults
runner_name=${runner_name:-$(hostname)}
svc_user=${svc_user:-$USER}
echo "Configuring runner @ ${runner_scope}" echo "Configuring runner @ ${runner_scope}"
sudo echo sudo echo
@@ -142,7 +174,7 @@ echo
echo "Configuring as a service ..." echo "Configuring as a service ..."
prefix="" prefix=""
if [ "${runner_plat}" == "linux" ]; then if [ "${runner_plat}" == "linux" ]; then
prefix="sudo " prefix="sudo "
fi fi
${prefix}./svc.sh install ${svc_user} ${prefix}./svc.sh install ${svc_user}

View File

@@ -51,7 +51,7 @@ fi
# Ensure offline # Ensure offline
#-------------------------------------- #--------------------------------------
runner_status=$(curl -s -X GET ${base_api_url}/${runner_scope}/actions/runners?per_page=100 -H "accept: application/vnd.github.everest-preview+json" -H "authorization: token ${RUNNER_CFG_PAT}" \ runner_status=$(curl -s -X GET ${base_api_url}/${runner_scope}/actions/runners?per_page=100 -H "accept: application/vnd.github.everest-preview+json" -H "authorization: token ${RUNNER_CFG_PAT}" \
| jq -M -j ".runners | .[] | [select(.name == \"${runner_name}\")] | .[0].status") | jq -M -j ".runners | .[] | select(.name == \"${runner_name}\") | .status")
if [ -z "${runner_status}" ]; then if [ -z "${runner_status}" ]; then
fatal "Could not find runner with name ${runner_name}" fatal "Could not find runner with name ${runner_name}"
@@ -67,7 +67,7 @@ fi
# Get id of runner to remove # Get id of runner to remove
#-------------------------------------- #--------------------------------------
runner_id=$(curl -s -X GET ${base_api_url}/${runner_scope}/actions/runners?per_page=100 -H "accept: application/vnd.github.everest-preview+json" -H "authorization: token ${RUNNER_CFG_PAT}" \ runner_id=$(curl -s -X GET ${base_api_url}/${runner_scope}/actions/runners?per_page=100 -H "accept: application/vnd.github.everest-preview+json" -H "authorization: token ${RUNNER_CFG_PAT}" \
| jq -M -j ".runners | .[] | [select(.name == \"${runner_name}\")] | .[0].id") | jq -M -j ".runners | .[] | select(.name == \"${runner_name}\") | .id")
if [ -z "${runner_id}" ]; then if [ -z "${runner_id}" ]; then
fatal "Could not find runner with name ${runner_name}" fatal "Could not find runner with name ${runner_name}"

View File

@@ -73,4 +73,4 @@ if [ "${runner_plat}" == "linux" ]; then
fi fi
${prefix}./svc.sh stop ${prefix}./svc.sh stop
${prefix}./svc.sh uninstall ${prefix}./svc.sh uninstall
${prefix}./config.sh remove --token $REMOVE_TOKEN ./config.sh remove --token $REMOVE_TOKEN

View File

@@ -0,0 +1 @@
de62d296708908cfd1236e58869aebbc2bae8a8c3d629276968542626c508e37

View File

@@ -0,0 +1 @@
44fcd0422dd98ed17d2c8e9057ff2260c50165f20674236a4ae7d2645a07df25

View File

@@ -0,0 +1 @@
e57652cf322ee16ce3af4f9e58f80858746b9e1e60279e991a3b3d9a6baf8d79

View File

@@ -0,0 +1 @@
bdd247b2ff3f51095524412e2ac588e7a87af805e114d6caf2368366ee7be1ea

View File

@@ -0,0 +1 @@
d23a0cb9f20c0aa1cddb7a39567cd097020cdeb06a1e952940601d1a405c53b8

View File

@@ -0,0 +1 @@
6ca4a0e1c50b7079ead05321dcf5835c1c25f23dc632add8c1c4667d416d103e

View File

@@ -0,0 +1 @@
b5951dc607d782d9c7571a7224e940eb0975bb23c54ff25c7afdbf959a417081

View File

@@ -0,0 +1 @@
af819e92011cc9cbca90e8299f9f7651f2cf6bf45b42920f9a4ca22795486147

View File

@@ -0,0 +1 @@
aa0e6bf4bfaabf48c962ea3b262dca042629ab332005f73d282faec908847036

View File

@@ -0,0 +1 @@
40328cff2b8229f9b578f32739183bd8f6aab481c21dadc052b09f1c7e8e4665

View File

@@ -340,9 +340,8 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel) {
elseif ($Runtime -eq "aspnetcore") { elseif ($Runtime -eq "aspnetcore") {
$VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version" $VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version"
} }
# Currently, the WindowsDesktop runtime is manufactured with the .Net core runtime
elseif ($Runtime -eq "windowsdesktop") { elseif ($Runtime -eq "windowsdesktop") {
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version" $VersionFileUrl = "$UncachedFeed/WindowsDesktop/$Channel/latest.version"
} }
elseif (-not $Runtime) { elseif (-not $Runtime) {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version" $VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
@@ -438,7 +437,16 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string
$PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" $PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
} }
elseif ($Runtime -eq "windowsdesktop") { elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
if ($SpecificVersion -match '^(\d+)\.(.*)$')
{
$majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5)
{
$PayloadURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
}
}
} }
elseif (-not $Runtime) { elseif (-not $Runtime) {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificProductVersion-win-$CLIArchitecture.zip" $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificProductVersion-win-$CLIArchitecture.zip"
@@ -480,7 +488,16 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion) {
$ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt" $ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt"
} }
elseif ($Runtime -eq "windowsdesktop") { elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt" $ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
if ($SpecificVersion -match '^(\d+)\.(.*)')
{
$majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5)
{
$ProductVersionTxtURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/productVersion.txt"
}
}
} }
elseif (-not $Runtime) { elseif (-not $Runtime) {
$ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt" $ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt"
@@ -885,10 +902,10 @@ Say "Note that the script does not resolve dependencies during installation."
Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies" Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies"
Say "Installation finished" Say "Installation finished"
# SIG # Begin signature block # SIG # Begin signature block
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor # MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD2c707qnCLOLIC # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCNsnhcJvx/hXmM
# n6Mu5Gr4+Xp68foyZlGlTycnycc5l6CCDYEwggX/MIID56ADAgECAhMzAAABh3IX # w8KjuvvIMDBFonhg9XJFc1QwfTyH4aCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
@@ -960,29 +977,29 @@ Say "Installation finished"
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgE/MRhWyu # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgpT/bxWwe
# Zg+EA2WKcxYC31nHVCTE6guHppZppc70RtkwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # aW0EinKMWCAzDXUjwXkIHldYzR6lw4/1Pc0wQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQBvcYCjRDXUYEIz9j2j0r4GFI2Y3g/CoNxDDBaeQ+gV # BgkqhkiG9w0BAQEFAASCAQCHd7sSQVq0YDg8QDx6/kLWn3s6jtvvIDCCgsO9spHM
# khO0fK0oLh18RbV271Mg6SF7X7+mXB5MnL68voVQDqHnsCYrIAuMF/AEpv9YuDDp # quPd4FPbG67DCsKDClekQs52qrtRO3Zo+JMnCw4j3bS+gZHzeJr2shbftOrpsFoD
# ZRJuqN7Vwg3HM02l/FyATBIMgf/V79aYzJL3jjtt9bRIyxk6aPU4XcwMeA4usnUQ # l7OPcUmtrqul9dkQCOp8t0MP3ls0n96/YyNy6lz4BAlTdkdDx957uAxalKaCIBzb
# rMhIiQz07DgfSrcQWe4AvGFAIvqTAKE4P944EZWWVnWI/10rvatEAefqJZX3XljW # R9QyppOKIfNFvwD4EI5KI6tpmSy/uH8SrRg7ZExAYZl6J6R18WkL7KHn649lPoAQ
# sK/6NY/0MyAyiILOuXbvVS0YFbHaR2qd1jUXbrY79fS+H4Ts6qnbufOkHQvmcDxs # ujwrIXH10xOJops45ILGzKWQcHmCzLJGYapL4VHUuK+73nT+9ZROGHdk/PyvIcdw
# 801wKLHumMdPTtMVzfVMCwPvrHP0wtzsFlmCcKjBbGpvoYIS8TCCEu0GCisGAQQB # iERa+C06v305t3DA+CuHFy1tvyw7IFF6RVbLZPwxrJjToYIS7jCCEuoGCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME # gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB # AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEINdeoXtuzW+Dihw6n+VdG+91si0f6TvWhJXaPtvW # MDEwDQYJYIZIAWUDBAIBBQAEIOCaTmvM1AP0WaEVqzKaaCu/R+bTlR4kCrM/ZXsb
# oF4cAgZfu+i3IT8YEzIwMjAxMjE3MDYzMDM2LjU0M1owBIACAfSggdSkgdEwgc4x # /eNOAgZgGeLsMwsYEzIwMjEwMjAzMjExNzQ5LjU5MVowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg # Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA # U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA
# AAEsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # AAEsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw # cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
@@ -1043,7 +1060,7 @@ Say "Installation finished"
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a # cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ # KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+ # cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT # NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4 # cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4
@@ -1052,27 +1069,27 @@ Say "Installation finished"
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOOFYaowIhgPMjAyMDEyMTcwODQ4NDJaGA8yMDIwMTIxODA4NDg0MlowdzA9Bgor # AOPFChkwIhgPMjAyMTAyMDMxNTQwMDlaGA8yMDIxMDIwNDE1NDAwOVowdDA6Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA44VhqgIBADAKAgEAAgIoWgIB/zAHAgEAAgISJTAK # BgEEAYRZCgQBMSwwKjAKAgUA48UKGQIBADAHAgEAAgIXmDAHAgEAAgIRyTAKAgUA
# AgUA44azKgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB # 48ZbmQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAB53NDoDDF4vqFWY # B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHeeznL2n6HWCjHH94Fl
# fwUnSvAy3z0CtqSFeA9RzDKGklPRwVkya5DtmVBDTZUbVQ2ST9hvRAVxhktfyVBZ # hcdW6TEXzq4XNgp1Gx1W9F8gJ4x+SwoV7elJZkwgGffcpHomLvIY/VSuzsl1NgtJ
# ewapGJsvwMhg7nnEqBOumt6TvueIZpbs+p5z//3+iFYGkT3YFQI0Gd2JkvgBxfs5 # TWM2UxoqSv58BBOrl4eGhH6kkg8Ucy2tdeK5T8cHa8pMkq2j9pFd2mRG/6VMk0dl
# +GptO6JKtiyA+zkKijxqXZvMqMxBMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC # Xz7Uy3Z6bZqkcABMyAfuAaGbMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUD # U3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUDBAIB
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B # BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# CQQxIgQg3wEUtEvxwCp3aAFB2vGXOOqg/AXHyXZh9P9J+0uArDMwgfoGCyqGSIb3 # IgQg/QYv7yp+354WTjWUIsXWndTEzXjaYjqwYjcBxCJKjdUwgfoGCyqGSIb3DQEJ
# DQEJEAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9n # EAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9nu5ZH
# u5ZH4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # 4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABLCKv
# LCKvRZd1+RvuAAAAAAEsMCIEINBRtGID6jvA2ptfwIuPyG7qPcLRYb9YrJ8aKfVg # RZd1+RvuAAAAAAEsMCIEIIfIM3YbzHswb/Kj/qq1l1cHA6QBl+gEXYanUNJomrpT
# TulFMA0GCSqGSIb3DQEBCwUABIIBACQQpFGWW6JmH5MTKwhaE/8+gyzI2bT8XJnA # MA0GCSqGSIb3DQEBCwUABIIBAAwdcXssUZGO7ho5+NHLjIxLtQk543aKGo+lrRMY
# t8k7PHFvEGA7whgp9eNgW+wWJm1gnsmswjx2l7FW4DLg9lghM8FK77JRCg7CJfse # Q9abE1h/AaaNJl0iGxX4IihNWyfovSfYL3L4eODUBAu68tWSxeceRfWNsb/ZZfUi
# dSbnTv81/4VhSXOAO0jMP2dALP7DF59vQmlDh50u8/Wu61ActMOt6cArkoUhBRXO # v89hpLssI/Gf1BEgNMA4zCuIGQiC8okusVumEpAhhvCEbSiTTTtBdolTnU/CAKui
# LnqOQCOEEku5Xy2ES9g9eUfLUvTvlWo6HiAq+cJnNV08QRBOnGWRxdwy8YJ5vwNW # oxaU3R9XkKh1F4oAM26+dJ1J2BLQXPs5afNvvedDsZWNQUPK1sFF3JRfzxiTrwBW
# Pwx0ZG3rTvMtGzOaW6Ve5O36H2ynoEdzCmpakeDaF2sZ86/LNERKyIXiykV/Uig1 # EJRyflev9gyDoqCHzippgb+6+eti1WTkcA9Q49GIT11S6LOAVqkSC9N7Nqf8ksh8
# SZh2VLY/Yni9SCVHbYgvTOCh5ZZE5eOi6BwLf0T4xl5alHUx+AA= # ARdwT8jigpsm+mj7lrVU9upDkhVYhKeO8oiZq95Q53Zkteo=
# SIG # End signature block # SIG # End signature block

View File

@@ -303,7 +303,7 @@ get_machine_architecture() {
echo "arm" echo "arm"
return 0 return 0
;; ;;
aarch64) aarch64|arm64)
echo "arm64" echo "arm64"
return 0 return 0
;; ;;
@@ -489,7 +489,7 @@ get_specific_version_from_version() {
local json_file="$5" local json_file="$5"
if [ -z "$json_file" ]; then if [ -z "$json_file" ]; then
if [[ "$version" == "latest" ]]; then if [[ "$version" == "latest" ]]; then
local version_info local version_info
version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1 version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
say_verbose "get_specific_version_from_version: version_info=$version_info" say_verbose "get_specific_version_from_version: version_info=$version_info"
@@ -522,7 +522,7 @@ construct_download_link() {
local specific_version="${4//[$'\t\r\n']}" local specific_version="${4//[$'\t\r\n']}"
local specific_product_version="$(get_specific_product_version "$1" "$4")" local specific_product_version="$(get_specific_product_version "$1" "$4")"
local osname="$5" local osname="$5"
local download_link=null local download_link=null
if [[ "$runtime" == "dotnet" ]]; then if [[ "$runtime" == "dotnet" ]]; then
download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz" download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz"
@@ -542,7 +542,7 @@ construct_download_link() {
# azure_feed - $1 # azure_feed - $1
# specific_version - $2 # specific_version - $2
get_specific_product_version() { get_specific_product_version() {
# If we find a 'productVersion.txt' at the root of any folder, we'll use its contents # If we find a 'productVersion.txt' at the root of any folder, we'll use its contents
# to resolve the version of what's in the folder, superseding the specified version. # to resolve the version of what's in the folder, superseding the specified version.
eval $invocation eval $invocation
@@ -744,13 +744,30 @@ download() {
fi fi
local failed=false local failed=false
if machine_has "curl"; then local attempts=0
downloadcurl "$remote_path" "$out_path" || failed=true while [ $attempts -lt 3 ]; do
elif machine_has "wget"; then attempts=$((attempts+1))
downloadwget "$remote_path" "$out_path" || failed=true failed=false
else if machine_has "curl"; then
failed=true downloadcurl "$remote_path" "$out_path" || failed=true
fi elif machine_has "wget"; then
downloadwget "$remote_path" "$out_path" || failed=true
else
say_err "Missing dependency: neither curl nor wget was found."
exit 1
fi
if [ "$failed" = false ] || [ $attempts -ge 3 ] || { [ ! -z $http_code ] && [ $http_code = "404" ]; }; then
break
fi
say "Download attempt #$attempts has failed: $http_code $download_error_msg"
say "Attempt #$((attempts+1)) will start in $((attempts*10)) seconds."
sleep $((attempts*20))
done
if [ "$failed" = true ]; then if [ "$failed" = true ]; then
say_verbose "Download failed: $remote_path" say_verbose "Download failed: $remote_path"
return 1 return 1
@@ -761,6 +778,8 @@ download() {
# Updates global variables $http_code and $download_error_msg # Updates global variables $http_code and $download_error_msg
downloadcurl() { downloadcurl() {
eval $invocation eval $invocation
unset http_code
unset download_error_msg
local remote_path="$1" local remote_path="$1"
local out_path="${2:-}" local out_path="${2:-}"
# Append feed_credential as late as possible before calling curl to avoid logging feed_credential # Append feed_credential as late as possible before calling curl to avoid logging feed_credential
@@ -789,6 +808,8 @@ downloadcurl() {
# Updates global variables $http_code and $download_error_msg # Updates global variables $http_code and $download_error_msg
downloadwget() { downloadwget() {
eval $invocation eval $invocation
unset http_code
unset download_error_msg
local remote_path="$1" local remote_path="$1"
local out_path="${2:-}" local out_path="${2:-}"
# Append feed_credential as late as possible before calling wget to avoid logging feed_credential # Append feed_credential as late as possible before calling wget to avoid logging feed_credential
@@ -882,12 +903,11 @@ install_dotnet() {
say "Downloading primary link $download_link" say "Downloading primary link $download_link"
# The download function will set variables $http_code and $download_error_msg in case of failure. # The download function will set variables $http_code and $download_error_msg in case of failure.
http_code=""; download_error_msg=""
download "$download_link" "$zip_path" 2>&1 || download_failed=true download "$download_link" "$zip_path" 2>&1 || download_failed=true
primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
# if the download fails, download the legacy_download_link # if the download fails, download the legacy_download_link
if [ "$download_failed" = true ]; then if [ "$download_failed" = true ]; then
primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
case $primary_path_http_code in case $primary_path_http_code in
404) 404)
say "The resource at $download_link is not available." say "The resource at $download_link is not available."
@@ -906,11 +926,10 @@ install_dotnet() {
say "Downloading legacy link $download_link" say "Downloading legacy link $download_link"
# The download function will set variables $http_code and $download_error_msg in case of failure. # The download function will set variables $http_code and $download_error_msg in case of failure.
http_code=""; download_error_msg=""
download "$download_link" "$zip_path" 2>&1 || download_failed=true download "$download_link" "$zip_path" 2>&1 || download_failed=true
legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
if [ "$download_failed" = true ]; then if [ "$download_failed" = true ]; then
legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
case $legacy_path_http_code in case $legacy_path_http_code in
404) 404)
say "The resource at $download_link is not available." say "The resource at $download_link is not available."
@@ -1112,10 +1131,10 @@ do
echo " --arch,-Architecture,-Arch" echo " --arch,-Architecture,-Arch"
echo " Possible values: x64, arm, and arm64" echo " Possible values: x64, arm, and arm64"
echo " --os <system> Specifies operating system to be used when selecting the installer." echo " --os <system> Specifies operating system to be used when selecting the installer."
echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6." echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6."
echo " In case any other value is provided, the platform will be determined by the script based on machine configuration." echo " In case any other value is provided, the platform will be determined by the script based on machine configuration."
echo " Not supported for legacy links. Use --runtime-id to specify platform for legacy links." echo " Not supported for legacy links. Use --runtime-id to specify platform for legacy links."
echo " Refer to: https://aka.ms/dotnet-os-lifecycle for more information." echo " Refer to: https://aka.ms/dotnet-os-lifecycle for more information."
echo " --runtime <RUNTIME> Installs a shared runtime only, without the SDK." echo " --runtime <RUNTIME> Installs a shared runtime only, without the SDK."
echo " -Runtime" echo " -Runtime"
echo " Possible values:" echo " Possible values:"
@@ -1140,7 +1159,7 @@ do
echo " Installs just the shared runtime bits, not the entire SDK." echo " Installs just the shared runtime bits, not the entire SDK."
echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)." echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
echo " -RuntimeId" The parameter is obsolete and may be removed in a future version of this script. Should be used only for versions below 2.1. echo " -RuntimeId" The parameter is obsolete and may be removed in a future version of this script. Should be used only for versions below 2.1.
echo " For primary links to override OS or/and architecture, use --os and --architecture option instead." echo " For primary links to override OS or/and architecture, use --os and --architecture option instead."
echo "" echo ""
echo "Install Location:" echo "Install Location:"
echo " Location is chosen in following order:" echo " Location is chosen in following order:"
@@ -1177,7 +1196,7 @@ if [ "$dry_run" = true ]; then
if [ "$valid_legacy_download_link" = true ]; then if [ "$valid_legacy_download_link" = true ]; then
say "Legacy named payload URL: $legacy_download_link" say "Legacy named payload URL: $legacy_download_link"
fi fi
repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\""" repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
if [[ "$runtime" == "dotnet" ]]; then if [[ "$runtime" == "dotnet" ]]; then
repeatable_command+=" --runtime "\""dotnet"\""" repeatable_command+=" --runtime "\""dotnet"\"""
elif [[ "$runtime" == "aspnetcore" ]]; then elif [[ "$runtime" == "aspnetcore" ]]; then

View File

@@ -1291,9 +1291,9 @@
} }
}, },
"glob-parent": { "glob-parent": {
"version": "5.1.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
@@ -1374,9 +1374,9 @@
"dev": true "dev": true
}, },
"hosted-git-info": { "hosted-git-info": {
"version": "2.8.8", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true "dev": true
}, },
"iconv-lite": { "iconv-lite": {
@@ -1683,9 +1683,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.19", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true "dev": true
}, },
"lodash.unescape": { "lodash.unescape": {
@@ -1947,9 +1947,9 @@
"dev": true "dev": true
}, },
"path-parse": { "path-parse": {
"version": "1.0.6", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true "dev": true
}, },
"path-type": { "path-type": {

View File

@@ -4,6 +4,7 @@ PRECACHE=$2
NODE_URL=https://nodejs.org/dist NODE_URL=https://nodejs.org/dist
NODE12_VERSION="12.13.1" NODE12_VERSION="12.13.1"
NODE16_VERSION="16.13.0"
get_abs_path() { get_abs_path() {
# exploits the fact that pwd will print abs path when no args # exploits the fact that pwd will print abs path when no args
@@ -126,6 +127,8 @@ function acquireExternalTool() {
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.exe" node12/bin acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.exe" node12/bin
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.lib" node12/bin acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.lib" node12/bin
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
if [[ "$PRECACHE" != "" ]]; then if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi fi
@@ -134,18 +137,23 @@ fi
# Download the external tools only for OSX. # Download the external tools only for OSX.
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
fi fi
# Download the external tools for Linux PACKAGERUNTIMEs. # Download the external tools for Linux PACKAGERUNTIMEs.
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE12_VERSION}/alpine/x64/node-${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE12_VERSION}/alpine/x64/node-${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE16_VERSION}/alpine/x64/node-v${NODE16_VERSION}-alpine-x64.tar.gz" node16_alpine
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
fi fi

View File

@@ -82,7 +82,8 @@ var gracefulShutdown = function (code) {
console.log('Sending SIGINT to runner listener to stop'); console.log('Sending SIGINT to runner listener to stop');
listener.kill('SIGINT'); listener.kill('SIGINT');
// TODO wait for 30 seconds and send a SIGKILL console.log('Sending SIGKILL to runner listener');
setTimeout(() => listener.kill('SIGKILL'), 30000);
} }
} }

View File

@@ -25,5 +25,7 @@
</dict> </dict>
<key>ProcessType</key> <key>ProcessType</key>
<string>Interactive</string> <string>Interactive</string>
<key>SessionCreate</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@@ -94,7 +94,6 @@ then
fi fi
} }
# libssl version prefer: libssl1.1 -> libssl1.0.2 -> libssl1.0.0
apt_get_with_fallbacks libssl1.1$ libssl1.0.2$ libssl1.0.0$ apt_get_with_fallbacks libssl1.1$ libssl1.0.2$ libssl1.0.0$
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
@@ -103,8 +102,7 @@ then
exit 1 exit 1
fi fi
# libicu version prefer: libicu66 -> libicu63 -> libicu60 -> libicu57 -> libicu55 -> libicu52 apt_get_with_fallbacks libicu72 libicu71 libicu70 libicu69 libicu68 libicu67 libicu66 libicu65 libicu63 libicu60 libicu57 libicu55 libicu52
apt_get_with_fallbacks libicu66 libicu63 libicu60 libicu57 libicu55 libicu52
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
echo "'$apt_get' failed with exit code '$?'" echo "'$apt_get' failed with exit code '$?'"

View File

@@ -106,25 +106,37 @@ function stop()
function uninstall() function uninstall()
{ {
stop if service_exists; then
systemctl disable ${SVC_NAME} || failed "failed to disable ${SVC_NAME}" stop
rm "${UNIT_PATH}" || failed "failed to delete ${UNIT_PATH}" systemctl disable ${SVC_NAME} || failed "failed to disable ${SVC_NAME}"
rm "${UNIT_PATH}" || failed "failed to delete ${UNIT_PATH}"
else
echo "Service ${SVC_NAME} is not installed"
fi
if [ -f "${CONFIG_PATH}" ]; then if [ -f "${CONFIG_PATH}" ]; then
rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}" rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}"
fi fi
systemctl daemon-reload || failed "failed to reload daemons" systemctl daemon-reload || failed "failed to reload daemons"
} }
function service_exists() {
if [ -f "${UNIT_PATH}" ]; then
return 0
else
return 1
fi
}
function status() function status()
{ {
if [ -f "${UNIT_PATH}" ]; then if service_exists; then
echo echo
echo "${UNIT_PATH}" echo "${UNIT_PATH}"
else else
echo echo
echo "not installed" echo "not installed"
echo echo
return exit 1
fi fi
systemctl --no-pager status ${SVC_NAME} systemctl --no-pager status ${SVC_NAME}

View File

@@ -18,6 +18,8 @@ downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
logfile="_UPDATE_LOG_" logfile="_UPDATE_LOG_"
restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_ restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
telemetryfile="$rootfolder/_diag/.telemetry"
# log user who run the script # log user who run the script
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1 date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
whoami >> "$logfile" 2>&1 whoami >> "$logfile" 2>&1
@@ -118,6 +120,58 @@ then
exit 1 exit 1
fi fi
# fix upgrade issue with macOS when running as a service
attemptedtargetedfix=0
currentplatform=$(uname | awk '{print tolower($0)}')
if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
# We needed a fix for https://github.com/actions/runner/issues/743
# We will recreate the ./externals/node12/bin/node of the past runner version that launched the runnerlistener service
# Otherwise mac gatekeeper kills the processes we spawn on creation as we are running a process with no backing file
# We need the pid for the nodejs loop, get that here, its the parent of the runner C# pid
# assumption here is only one process is invoking rootfolder/runsvc.sh
procgroup=$(ps x -o pgid,command | grep "$rootfolder/runsvc.sh" | grep -v grep | awk '{print $1}')
if [[ $? -eq 0 && -n "$procgroup" ]]
then
# inspect the open file handles to find the node process
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
path=$(lsof -a -g "$procgroup" -F n | grep node12/bin/node | grep externals | tail -1 | cut -c2-)
if [[ $? -eq 0 && -n "$path" ]]
then
# trim the last 5 characters of the path '/node'
trimmedpath=$(dirname "$path")
if [[ $? -eq 0 && -n "$trimmedpath" ]]
then
attemptedtargetedfix=1
# Create the path if it does not exist
if [[ ! -e "$path" ]]
then
date "+[%F %T-%4N] Creating fallback node at path $path" >> "$logfile" 2>&1
mkdir -p "$trimmedpath"
cp "$rootfolder/externals/node12/bin/node" "$path"
else
date "+[%F %T-%4N] Path for fallback node exists, skipping creating $path" >> "$logfile" 2>&1
fi
else
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to trim runner path. TrimmedPath: $trimmedpath, path: $path, pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to trim runner path. TrimmedPath: $trimmedpath, path: $path, pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
fi
else
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner path. Path: $path, pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner path. Path: $path, pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
fi
else
runproc=$(ps x -o pgid,command | grep "run.sh" | grep -v grep | awk '{print $1}')
if [[ $? -eq 0 && -n "$runproc" ]]
then
date "+[%F %T-%4N] Running as ephemeral using run.sh, no need to recreate node folder" >> "$logfile" 2>&1
else
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner pgid. pgid: $procgroup, root: $rootfolder" >> "$logfile" 2>&1
date "+[%F %T-%4N] DarwinRunnerUpgrade: Failed to find runner pgid. pgid: $procgroup, root: $rootfolder" >> "$telemetryfile" 2>&1
fi
fi
fi
date "+[%F %T-%4N] Update succeed" >> "$logfile" date "+[%F %T-%4N] Update succeed" >> "$logfile"
# rename the update log file with %logfile%.succeed/.failed/succeedneedrestart # rename the update log file with %logfile%.succeed/.failed/succeedneedrestart

View File

@@ -8,7 +8,7 @@ if [ $user_id -eq 0 -a -z "$RUNNER_ALLOW_RUNASROOT" ]; then
exit 1 exit 1
fi fi
# Check dotnet core 3.0 dependencies for Linux # Check dotnet Core 6.0 dependencies for Linux
if [[ (`uname` == "Linux") ]] if [[ (`uname` == "Linux") ]]
then then
command -v ldd > /dev/null command -v ldd > /dev/null
@@ -18,25 +18,25 @@ then
exit 1 exit 1
fi fi
message="Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies." message="Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies."
ldd ./bin/libcoreclr.so | grep 'not found' ldd ./bin/libcoreclr.so | grep 'not found'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 3.0" echo "Dependencies is missing for Dotnet Core 6.0"
echo $message echo $message
exit 1 exit 1
fi fi
ldd ./bin/System.Security.Cryptography.Native.OpenSsl.so | grep 'not found' ldd ./bin/libSystem.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 3.0" echo "Dependencies is missing for Dotnet Core 6.0"
echo $message echo $message
exit 1 exit 1
fi fi
ldd ./bin/System.IO.Compression.Native.so | grep 'not found' ldd ./bin/libSystem.IO.Compression.Native.so | grep 'not found'
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 3.0" echo "Dependencies is missing for Dotnet Core 6.0"
echo $message echo $message
exit 1 exit 1
fi fi
@@ -54,7 +54,7 @@ then
libpath=${LD_LIBRARY_PATH:-} libpath=${LD_LIBRARY_PATH:-}
$LDCONFIG_COMMAND -NXv ${libpath//:/ } 2>&1 | grep libicu >/dev/null 2>&1 $LDCONFIG_COMMAND -NXv ${libpath//:/ } 2>&1 | grep libicu >/dev/null 2>&1
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Libicu's dependencies is missing for Dotnet Core 3.0" echo "Libicu's dependencies is missing for Dotnet Core 6.0"
echo $message echo $message
exit 1 exit 1
fi fi

View File

@@ -43,6 +43,21 @@ else
else else
sleep 5 sleep 5
fi fi
elif [[ $returnCode == 4 ]]; then
if [ ! -x "$(command -v sleep)" ]; then
if [ ! -x "$(command -v ping)" ]; then
COUNT="0"
while [[ $COUNT != 5000 ]]; do
echo "SLEEP" > /dev/null
COUNT=$[$COUNT+1]
done
else
ping -c 5 127.0.0.1 > /dev/null
fi
else
sleep 5
fi
"$DIR"/bin/Runner.Listener run $*
else else
exit $returnCode exit $returnCode
fi fi

57
src/Misc/runnercoreassets Normal file
View File

@@ -0,0 +1,57 @@
actions.runner.plist.template
actions.runner.service.template
checkScripts/downloadCert.js
checkScripts/makeWebRequest.js
darwin.svc.sh.template
hashFiles/index.js
installdependencies.sh
macos-run-invoker.js
Microsoft.IdentityModel.Logging.dll
Microsoft.IdentityModel.Tokens.dll
Minimatch.dll
Newtonsoft.Json.Bson.dll
Newtonsoft.Json.dll
Runner.Common.deps.json
Runner.Common.dll
Runner.Common.pdb
Runner.Listener
Runner.Listener.deps.json
Runner.Listener.dll
Runner.Listener.exe
Runner.Listener.pdb
Runner.Listener.runtimeconfig.json
Runner.PluginHost
Runner.PluginHost.deps.json
Runner.PluginHost.dll
Runner.PluginHost.exe
Runner.PluginHost.pdb
Runner.PluginHost.runtimeconfig.json
Runner.Plugins.deps.json
Runner.Plugins.dll
Runner.Plugins.pdb
Runner.Sdk.deps.json
Runner.Sdk.dll
Runner.Sdk.pdb
Runner.Worker
Runner.Worker.deps.json
Runner.Worker.dll
Runner.Worker.exe
Runner.Worker.pdb
Runner.Worker.runtimeconfig.json
RunnerService.exe
RunnerService.exe.config
RunnerService.js
RunnerService.pdb
runsvc.sh
Sdk.deps.json
Sdk.dll
Sdk.pdb
System.IdentityModel.Tokens.Jwt.dll
System.Net.Http.Formatting.dll
System.Security.Cryptography.Pkcs.dll
System.Security.Cryptography.ProtectedData.dll
System.ServiceProcess.ServiceController.dll
systemd.svc.sh.template
update.cmd.template
update.sh.template
YamlDotNet.dll

View File

@@ -0,0 +1,263 @@
api-ms-win-core-console-l1-1-0.dll
api-ms-win-core-console-l1-2-0.dll
api-ms-win-core-datetime-l1-1-0.dll
api-ms-win-core-debug-l1-1-0.dll
api-ms-win-core-errorhandling-l1-1-0.dll
api-ms-win-core-file-l1-1-0.dll
api-ms-win-core-file-l1-2-0.dll
api-ms-win-core-file-l2-1-0.dll
api-ms-win-core-handle-l1-1-0.dll
api-ms-win-core-heap-l1-1-0.dll
api-ms-win-core-interlocked-l1-1-0.dll
api-ms-win-core-libraryloader-l1-1-0.dll
api-ms-win-core-localization-l1-2-0.dll
api-ms-win-core-memory-l1-1-0.dll
api-ms-win-core-namedpipe-l1-1-0.dll
api-ms-win-core-processenvironment-l1-1-0.dll
api-ms-win-core-processthreads-l1-1-0.dll
api-ms-win-core-processthreads-l1-1-1.dll
api-ms-win-core-profile-l1-1-0.dll
api-ms-win-core-rtlsupport-l1-1-0.dll
api-ms-win-core-string-l1-1-0.dll
api-ms-win-core-synch-l1-1-0.dll
api-ms-win-core-synch-l1-2-0.dll
api-ms-win-core-sysinfo-l1-1-0.dll
api-ms-win-core-timezone-l1-1-0.dll
api-ms-win-core-util-l1-1-0.dll
api-ms-win-crt-conio-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-multibyte-l1-1-0.dll
api-ms-win-crt-private-l1-1-0.dll
api-ms-win-crt-process-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
api-ms-win-crt-utility-l1-1-0.dll
clrcompression.dll
clretwrc.dll
clrjit.dll
coreclr.dll
createdump
createdump.exe
dbgshim.dll
hostfxr.dll
hostpolicy.dll
libclrjit.dylib
libclrjit.so
libcoreclr.dylib
libcoreclr.so
libcoreclrtraceptprovider.so
libdbgshim.dylib
libdbgshim.so
libhostfxr.dylib
libhostfxr.so
libhostpolicy.dylib
libhostpolicy.so
libmscordaccore.dylib
libmscordaccore.so
libmscordbi.dylib
libmscordbi.so
Microsoft.CSharp.dll
Microsoft.DiaSymReader.Native.amd64.dll
Microsoft.VisualBasic.Core.dll
Microsoft.VisualBasic.dll
Microsoft.Win32.Primitives.dll
Microsoft.Win32.Registry.dll
mscordaccore.dll
mscordaccore_amd64_amd64_6.0.21.52210.dll
mscordbi.dll
mscorlib.dll
mscorrc.debug.dll
mscorrc.dll
msquic.dll
netstandard.dll
SOS_README.md
System.AppContext.dll
System.Buffers.dll
System.Collections.Concurrent.dll
System.Collections.dll
System.Collections.Immutable.dll
System.Collections.NonGeneric.dll
System.Collections.Specialized.dll
System.ComponentModel.Annotations.dll
System.ComponentModel.DataAnnotations.dll
System.ComponentModel.dll
System.ComponentModel.EventBasedAsync.dll
System.ComponentModel.Primitives.dll
System.ComponentModel.TypeConverter.dll
System.Configuration.dll
System.Console.dll
System.Core.dll
System.Data.Common.dll
System.Data.DataSetExtensions.dll
System.Data.dll
System.Diagnostics.Contracts.dll
System.Diagnostics.Debug.dll
System.Diagnostics.DiagnosticSource.dll
System.Diagnostics.FileVersionInfo.dll
System.Diagnostics.Process.dll
System.Diagnostics.StackTrace.dll
System.Diagnostics.TextWriterTraceListener.dll
System.Diagnostics.Tools.dll
System.Diagnostics.TraceSource.dll
System.Diagnostics.Tracing.dll
System.dll
System.Drawing.dll
System.Drawing.Primitives.dll
System.Dynamic.Runtime.dll
System.Formats.Asn1.dll
System.Globalization.Calendars.dll
System.Globalization.dll
System.Globalization.Extensions.dll
System.Globalization.Native.dylib
System.Globalization.Native.so
System.IO.Compression.Brotli.dll
System.IO.Compression.dll
System.IO.Compression.FileSystem.dll
System.IO.Compression.Native.a
System.IO.Compression.Native.dll
System.IO.Compression.Native.dylib
System.IO.Compression.Native.so
System.IO.Compression.ZipFile.dll
System.IO.dll
System.IO.FileSystem.AccessControl.dll
System.IO.FileSystem.dll
System.IO.FileSystem.DriveInfo.dll
System.IO.FileSystem.Primitives.dll
System.IO.FileSystem.Watcher.dll
System.IO.IsolatedStorage.dll
System.IO.MemoryMappedFiles.dll
System.IO.Pipes.AccessControl.dll
System.IO.Pipes.dll
System.IO.UnmanagedMemoryStream.dll
System.Linq.dll
System.Linq.Expressions.dll
System.Linq.Parallel.dll
System.Linq.Queryable.dll
System.Memory.dll
System.Native.a
System.Native.dylib
System.Native.so
System.Net.dll
System.Net.Http.dll
System.Net.Http.Json.dll
System.Net.Http.Native.a
System.Net.Http.Native.dylib
System.Net.Http.Native.so
System.Net.HttpListener.dll
System.Net.Mail.dll
System.Net.NameResolution.dll
System.Net.NetworkInformation.dll
System.Net.Ping.dll
System.Net.Primitives.dll
System.Net.Quic.dll
System.Net.Requests.dll
System.Net.Security.dll
System.Net.Security.Native.a
System.Net.Security.Native.dylib
System.Net.Security.Native.so
System.Net.ServicePoint.dll
System.Net.Sockets.dll
System.Net.WebClient.dll
System.Net.WebHeaderCollection.dll
System.Net.WebProxy.dll
System.Net.WebSockets.Client.dll
System.Net.WebSockets.dll
System.Numerics.dll
System.Numerics.Vectors.dll
System.ObjectModel.dll
System.Private.CoreLib.dll
System.Private.DataContractSerialization.dll
System.Private.Uri.dll
System.Private.Xml.dll
System.Private.Xml.Linq.dll
System.Reflection.DispatchProxy.dll
System.Reflection.dll
System.Reflection.Emit.dll
System.Reflection.Emit.ILGeneration.dll
System.Reflection.Emit.Lightweight.dll
System.Reflection.Extensions.dll
System.Reflection.Metadata.dll
System.Reflection.Primitives.dll
System.Reflection.TypeExtensions.dll
System.Resources.Reader.dll
System.Resources.ResourceManager.dll
System.Resources.Writer.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Runtime.CompilerServices.VisualC.dll
System.Runtime.dll
System.Runtime.Extensions.dll
System.Runtime.Handles.dll
System.Runtime.InteropServices.dll
System.Runtime.InteropServices.RuntimeInformation.dll
System.Runtime.InteropServices.WindowsRuntime.dll
System.Runtime.Intrinsics.dll
System.Runtime.Loader.dll
System.Runtime.Numerics.dll
System.Runtime.Serialization.dll
System.Runtime.Serialization.Formatters.dll
System.Runtime.Serialization.Json.dll
System.Runtime.Serialization.Primitives.dll
System.Runtime.Serialization.Xml.dll
System.Runtime.WindowsRuntime.dll
System.Runtime.WindowsRuntime.UI.Xaml.dll
System.Security.AccessControl.dll
System.Security.Claims.dll
System.Security.Cryptography.Algorithms.dll
System.Security.Cryptography.Cng.dll
System.Security.Cryptography.Csp.dll
System.Security.Cryptography.Encoding.dll
System.Security.Cryptography.Native.Apple.a
System.Security.Cryptography.Native.Apple.dylib
System.Security.Cryptography.Native.OpenSsl.a
System.Security.Cryptography.Native.OpenSsl.dylib
System.Security.Cryptography.Native.OpenSsl.so
System.Security.Cryptography.OpenSsl.dll
System.Security.Cryptography.Primitives.dll
System.Security.Cryptography.X509Certificates.dll
System.Security.Cryptography.XCertificates.dll
System.Security.dll
System.Security.Principal.dll
System.Security.Principal.Windows.dll
System.Security.SecureString.dll
System.ServiceModel.Web.dll
System.ServiceProcess.dll
System.Text.Encoding.CodePages.dll
System.Text.Encoding.dll
System.Text.Encoding.Extensions.dll
System.Text.Encodings.Web.dll
System.Text.Json.dll
System.Text.RegularExpressions.dll
System.Threading.Channels.dll
System.Threading.dll
System.Threading.Overlapped.dll
System.Threading.Tasks.Dataflow.dll
System.Threading.Tasks.dll
System.Threading.Tasks.Extensions.dll
System.Threading.Tasks.Parallel.dll
System.Threading.Thread.dll
System.Threading.ThreadPool.dll
System.Threading.Timer.dll
System.Transactions.dll
System.Transactions.Local.dll
System.ValueTuple.dll
System.Web.dll
System.Web.HttpUtility.dll
System.Windows.dll
System.Xml.dll
System.Xml.Linq.dll
System.Xml.ReaderWriter.dll
System.Xml.Serialization.dll
System.Xml.XDocument.dll
System.Xml.XmlDocument.dll
System.Xml.XmlSerializer.dll
System.Xml.XPath.dll
System.Xml.XPath.XDocument.dll
ucrtbase.dll
WindowsBase.dll

View File

@@ -0,0 +1,24 @@
[
{
"HashValue": "<NO_RUNTIME_EXTERNALS_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noruntime-noexternals.tar.gz",
"TrimmedContents": {
"dotnetRuntime": "<RUNTIME_HASH>",
"externals": "<EXTERNALS_HASH>"
}
},
{
"HashValue": "<NO_RUNTIME_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noruntime.tar.gz",
"TrimmedContents": {
"dotnetRuntime": "<RUNTIME_HASH>"
}
},
{
"HashValue": "<NO_EXTERNALS_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noexternals.tar.gz",
"TrimmedContents": {
"externals": "<EXTERNALS_HASH>"
}
}
]

View File

@@ -0,0 +1,24 @@
[
{
"HashValue": "<NO_RUNTIME_EXTERNALS_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noruntime-noexternals.zip",
"TrimmedContents": {
"dotnetRuntime": "<RUNTIME_HASH>",
"externals": "<EXTERNALS_HASH>"
}
},
{
"HashValue": "<NO_RUNTIME_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noruntime.zip",
"TrimmedContents": {
"dotnetRuntime": "<RUNTIME_HASH>"
}
},
{
"HashValue": "<NO_EXTERNALS_HASH>",
"DownloadUrl": "https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-<RUNNER_PLATFORM>-<RUNNER_VERSION>-noexternals.zip",
"TrimmedContents": {
"externals": "<EXTERNALS_HASH>"
}
}
]

View File

@@ -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; }

View File

@@ -26,6 +26,7 @@ namespace GitHub.Runner.Common
Certificates, Certificates,
Options, Options,
SetupInfo, SetupInfo,
Telemetry
} }
public static class Constants public static class Constants
@@ -41,6 +42,8 @@ namespace GitHub.Runner.Common
public static string PluginTracePrefix = "##[plugin.trace]"; public static string PluginTracePrefix = "##[plugin.trace]";
public static readonly int RunnerDownloadRetryMaxAttempts = 3; public static readonly int RunnerDownloadRetryMaxAttempts = 3;
public static readonly int CompositeActionsMaxDepth = 9;
// This enum is embedded within the Constants class to make it easier to reference and avoid // This enum is embedded within the Constants class to make it easier to reference and avoid
// ambiguous type reference with System.Runtime.InteropServices.OSPlatform and System.Runtime.InteropServices.Architecture // ambiguous type reference with System.Runtime.InteropServices.OSPlatform and System.Runtime.InteropServices.Architecture
public enum OSPlatform public enum OSPlatform
@@ -123,9 +126,10 @@ namespace GitHub.Runner.Common
{ {
public static readonly string Check = "check"; public static readonly string Check = "check";
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 Once = "once"; // Keep this around since customers still relies on it
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";
@@ -151,6 +155,7 @@ namespace GitHub.Runner.Common
public static readonly string LowDiskSpace = "LOW_DISK_SPACE"; public static readonly string LowDiskSpace = "LOW_DISK_SPACE";
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND"; public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/"; public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
} }
public static class RunnerEvent public static class RunnerEvent
@@ -210,6 +215,7 @@ namespace GitHub.Runner.Common
// Keep alphabetical // Keep alphabetical
// //
public static readonly string AllowUnsupportedCommands = "ACTIONS_ALLOW_UNSECURE_COMMANDS"; public static readonly string AllowUnsupportedCommands = "ACTIONS_ALLOW_UNSECURE_COMMANDS";
public static readonly string AllowUnsupportedStopCommandTokens = "ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS";
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG"; public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG"; public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
} }

View File

@@ -51,6 +51,7 @@ namespace GitHub.Runner.Common
Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.RemoveMatcherCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.WarningCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.WarningCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.ErrorCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.ErrorCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.NoticeCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.DebugCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.DebugCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.GroupCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.GroupCommandExtension, Runner.Worker");
Add<T>(extensions, "GitHub.Runner.Worker.EndGroupCommandExtension, Runner.Worker"); Add<T>(extensions, "GitHub.Runner.Worker.EndGroupCommandExtension, Runner.Worker");

View File

@@ -84,25 +84,28 @@ namespace GitHub.Runner.Common
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift1); this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift1);
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift2); this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift2);
this.SecretMasker.AddValueEncoder(ValueEncoders.CommandLineArgumentEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.ExpressionStringEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.ExpressionStringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.UriDataEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.XmlDataEscape); this.SecretMasker.AddValueEncoder(ValueEncoders.XmlDataEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.TrimDoubleQuotes); this.SecretMasker.AddValueEncoder(ValueEncoders.TrimDoubleQuotes);
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPreAmpersandEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.PowerShellPostAmpersandEscape);
// Create the trace manager. // Create the trace manager.
if (string.IsNullOrEmpty(logFile)) if (string.IsNullOrEmpty(logFile))
{ {
int logPageSize; int logPageSize;
string logSizeEnv = Environment.GetEnvironmentVariable($"{hostType.ToUpperInvariant()}_LOGSIZE"); string logSizeEnv = Environment.GetEnvironmentVariable($"{hostType.ToUpperInvariant()}_LOGSIZE");
if (!string.IsNullOrEmpty(logSizeEnv) || !int.TryParse(logSizeEnv, out logPageSize)) if (string.IsNullOrEmpty(logSizeEnv) || !int.TryParse(logSizeEnv, out logPageSize))
{ {
logPageSize = _defaultLogPageSize; logPageSize = _defaultLogPageSize;
} }
int logRetentionDays; int logRetentionDays;
string logRetentionDaysEnv = Environment.GetEnvironmentVariable($"{hostType.ToUpperInvariant()}_LOGRETENTION"); string logRetentionDaysEnv = Environment.GetEnvironmentVariable($"{hostType.ToUpperInvariant()}_LOGRETENTION");
if (!string.IsNullOrEmpty(logRetentionDaysEnv) || !int.TryParse(logRetentionDaysEnv, out logRetentionDays)) if (string.IsNullOrEmpty(logRetentionDaysEnv) || !int.TryParse(logRetentionDaysEnv, out logRetentionDays))
{ {
logRetentionDays = _defaultLogRetentionDays; logRetentionDays = _defaultLogRetentionDays;
} }
@@ -197,9 +200,17 @@ namespace GitHub.Runner.Common
if (credData != null && if (credData != null &&
credData.Data.TryGetValue("clientId", out var clientId)) credData.Data.TryGetValue("clientId", out var clientId))
{ {
_userAgents.Add(new ProductInfoHeaderValue($"RunnerId", clientId)); _userAgents.Add(new ProductInfoHeaderValue("ClientId", clientId));
} }
} }
var runnerFile = GetConfigFile(WellKnownConfigFile.Runner);
if (File.Exists(runnerFile))
{
var runnerSettings = IOUtil.LoadObject<RunnerSettings>(runnerFile);
_userAgents.Add(new ProductInfoHeaderValue("RunnerId", runnerSettings.AgentId.ToString(CultureInfo.InvariantCulture)));
_userAgents.Add(new ProductInfoHeaderValue("GroupId", runnerSettings.PoolId.ToString(CultureInfo.InvariantCulture)));
}
} }
public string GetDirectory(WellKnownDirectory directory) public string GetDirectory(WellKnownDirectory directory)
@@ -339,6 +350,12 @@ namespace GitHub.Runner.Common
GetDirectory(WellKnownDirectory.Root), GetDirectory(WellKnownDirectory.Root),
".setup_info"); ".setup_info");
break; break;
case WellKnownConfigFile.Telemetry:
path = Path.Combine(
GetDirectory(WellKnownDirectory.Diag),
".telemetry");
break;
default: default:
throw new NotSupportedException($"Unexpected well known config file: '{configFile}'"); throw new NotSupportedException($"Unexpected well known config file: '{configFile}'");

View File

@@ -2,8 +2,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using GitHub.Services.WebApi; using GitHub.Services.WebApi;
namespace GitHub.Runner.Common namespace GitHub.Runner.Common
@@ -35,7 +38,11 @@ namespace GitHub.Runner.Common
public async Task ConnectAsync(VssConnection jobConnection) public async Task ConnectAsync(VssConnection jobConnection)
{ {
_connection = jobConnection; _connection = jobConnection;
int attemptCount = 5; int totalAttempts = 5;
int attemptCount = totalAttempts;
var configurationStore = HostContext.GetService<IConfigurationStore>();
var runnerSettings = configurationStore.GetSettings();
while (!_connection.HasAuthenticated && attemptCount-- > 0) while (!_connection.HasAuthenticated && attemptCount-- > 0)
{ {
try try
@@ -45,17 +52,71 @@ namespace GitHub.Runner.Common
} }
catch (Exception ex) when (attemptCount > 0) catch (Exception ex) when (attemptCount > 0)
{ {
Trace.Info($"Catch exception during connect. {attemptCount} attemp left."); Trace.Info($"Catch exception during connect. {attemptCount} attempts left.");
Trace.Error(ex); Trace.Error(ex);
if (runnerSettings.IsHostedServer)
{
await CheckNetworkEndpointsAsync(attemptCount);
}
} }
await Task.Delay(100); int attempt = totalAttempts - attemptCount;
TimeSpan backoff = BackoffTimerHelper.GetExponentialBackoff(attempt, TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(3.2), TimeSpan.FromMilliseconds(100));
await Task.Delay(backoff);
} }
_taskClient = _connection.GetClient<TaskHttpClient>(); _taskClient = _connection.GetClient<TaskHttpClient>();
_hasConnection = true; _hasConnection = true;
} }
private async Task CheckNetworkEndpointsAsync(int attemptsLeft)
{
try
{
Trace.Info("Requesting Actions Service health endpoint status");
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
using (var actionsClient = new HttpClient(httpClientHandler))
{
var baseUri = new Uri(_connection.Uri.GetLeftPart(UriPartial.Authority));
actionsClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
// Call the _apis/health endpoint, and include how many attempts are left as a URL query for easy tracking
var response = await actionsClient.GetAsync(new Uri(baseUri, $"_apis/health?_internalRunnerAttemptsLeft={attemptsLeft}"));
Trace.Info($"Actions health status code: {response.StatusCode}");
}
}
catch (Exception ex)
{
// Log error, but continue as this call is best-effort
Trace.Info($"Actions Service health endpoint failed due to {ex.GetType().Name}");
Trace.Error(ex);
}
try
{
Trace.Info("Requesting Github API endpoint status");
// This is a dotcom public API... just call it directly
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
using (var gitHubClient = new HttpClient(httpClientHandler))
{
gitHubClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
// Call the api.github.com endpoint, and include how many attempts are left as a URL query for easy tracking
var response = await gitHubClient.GetAsync($"https://api.github.com?_internalRunnerAttemptsLeft={attemptsLeft}");
Trace.Info($"api.github.com status code: {response.StatusCode}");
}
}
catch (Exception ex)
{
// Log error, but continue as this call is best-effort
Trace.Info($"Github API endpoint failed due to {ex.GetType().Name}");
Trace.Error(ex);
}
}
private void CheckConnection() private void CheckConnection()
{ {
if (!_hasConnection) if (!_hasConnection)

View File

@@ -15,6 +15,7 @@ namespace GitHub.Runner.Common
[ServiceLocator(Default = typeof(JobServerQueue))] [ServiceLocator(Default = typeof(JobServerQueue))]
public interface IJobServerQueue : IRunnerService, IThrottlingReporter public interface IJobServerQueue : IRunnerService, IThrottlingReporter
{ {
TaskCompletionSource<int> JobRecordUpdated { get; }
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling; event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
Task ShutdownAsync(); Task ShutdownAsync();
void Start(Pipelines.AgentJobRequestMessage jobRequest); void Start(Pipelines.AgentJobRequestMessage jobRequest);
@@ -62,8 +63,11 @@ namespace GitHub.Runner.Common
private IJobServer _jobServer; private IJobServer _jobServer;
private Task[] _allDequeueTasks; private Task[] _allDequeueTasks;
private readonly TaskCompletionSource<int> _jobCompletionSource = new TaskCompletionSource<int>(); private readonly TaskCompletionSource<int> _jobCompletionSource = new TaskCompletionSource<int>();
private readonly TaskCompletionSource<int> _jobRecordUpdated = new TaskCompletionSource<int>();
private bool _queueInProcess = false; private bool _queueInProcess = false;
public TaskCompletionSource<int> JobRecordUpdated => _jobRecordUpdated;
public event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling; public event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
// Web console dequeue will start with process queue every 250ms for the first 60*4 times (~60 seconds). // Web console dequeue will start with process queue every 250ms for the first 60*4 times (~60 seconds).
@@ -287,11 +291,11 @@ namespace GitHub.Runner.Common
{ {
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber.Value, default(CancellationToken)); await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber.Value, default(CancellationToken));
} }
else else
{ {
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), default(CancellationToken)); await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), default(CancellationToken));
} }
if (_firstConsoleOutputs) if (_firstConsoleOutputs)
{ {
HostContext.WritePerfCounter($"WorkerJobServerQueueAppendFirstConsoleOutput_{_planId.ToString()}"); HostContext.WritePerfCounter($"WorkerJobServerQueueAppendFirstConsoleOutput_{_planId.ToString()}");
@@ -455,6 +459,14 @@ namespace GitHub.Runner.Common
{ {
Trace.Verbose("Cleanup buffered timeline record for timeline: {0}.", update.TimelineId); Trace.Verbose("Cleanup buffered timeline record for timeline: {0}.", update.TimelineId);
} }
if (!_jobRecordUpdated.Task.IsCompleted &&
update.PendingRecords.Any(x => x.Id == _jobTimelineRecordId && x.State != null))
{
// We have changed the state of the job
Trace.Info("Job timeline record has been updated for the first time.");
_jobRecordUpdated.TrySetResult(0);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -544,6 +556,11 @@ namespace GitHub.Runner.Common
timelineRecord.WarningCount = rec.WarningCount; timelineRecord.WarningCount = rec.WarningCount;
} }
if (rec.NoticeCount != null && rec.NoticeCount > 0)
{
timelineRecord.NoticeCount = rec.NoticeCount;
}
if (rec.Issues.Count > 0) if (rec.Issues.Count > 0)
{ {
timelineRecord.Issues.Clear(); timelineRecord.Issues.Clear();

View File

@@ -101,7 +101,7 @@ namespace GitHub.Runner.Common
EndPage(); EndPage();
_byteCount = 0; _byteCount = 0;
_dataFileName = Path.Combine(_pagesFolder, $"{_timelineId}_{_timelineRecordId}_{++_pageCount}.log"); _dataFileName = Path.Combine(_pagesFolder, $"{_timelineId}_{_timelineRecordId}_{++_pageCount}.log");
_pageData = new FileStream(_dataFileName, FileMode.CreateNew); _pageData = new FileStream(_dataFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite);
_pageWriter = new StreamWriter(_pageData, System.Text.Encoding.UTF8); _pageWriter = new StreamWriter(_pageData, System.Text.Encoding.UTF8);
} }

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Common namespace GitHub.Runner.Common
{ {
@@ -68,6 +69,7 @@ namespace GitHub.Runner.Common
public async Task SendAsync(MessageType messageType, string body, CancellationToken cancellationToken) public async Task SendAsync(MessageType messageType, string body, CancellationToken cancellationToken)
{ {
Trace.Info($"Sending message of length {body.Length}, with hash '{IOUtil.GetSha256Hash(body)}'");
await _writeStream.WriteInt32Async((int)messageType, cancellationToken); await _writeStream.WriteInt32Async((int)messageType, cancellationToken);
await _writeStream.WriteStringAsync(body, cancellationToken); await _writeStream.WriteStringAsync(body, cancellationToken);
} }
@@ -77,6 +79,7 @@ namespace GitHub.Runner.Common
WorkerMessage result = new WorkerMessage(MessageType.NotInitialized, string.Empty); WorkerMessage result = new WorkerMessage(MessageType.NotInitialized, string.Empty);
result.MessageType = (MessageType)await _readStream.ReadInt32Async(cancellationToken); result.MessageType = (MessageType)await _readStream.ReadInt32Async(cancellationToken);
result.Body = await _readStream.ReadStringAsync(cancellationToken); result.Body = await _readStream.ReadStringAsync(cancellationToken);
Trace.Info($"Receiving message of length {result.Body.Length}, with hash '{IOUtil.GetSha256Hash(result.Body)}'");
return result; return result;
} }

View File

@@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -29,8 +29,10 @@ namespace GitHub.Runner.Common
// Configuration // Configuration
Task<TaskAgent> AddAgentAsync(Int32 agentPoolId, TaskAgent agent); Task<TaskAgent> AddAgentAsync(Int32 agentPoolId, TaskAgent agent);
Task DeleteAgentAsync(int agentPoolId, int agentId); Task DeleteAgentAsync(int agentPoolId, int agentId);
Task DeleteAgentAsync(int agentId);
Task<List<TaskAgentPool>> GetAgentPoolsAsync(string agentPoolName = null, TaskAgentPoolType poolType = TaskAgentPoolType.Automation); Task<List<TaskAgentPool>> GetAgentPoolsAsync(string agentPoolName = null, TaskAgentPoolType poolType = TaskAgentPoolType.Automation);
Task<List<TaskAgent>> GetAgentsAsync(int agentPoolId, string agentName = null); Task<List<TaskAgent>> GetAgentsAsync(int agentPoolId, string agentName = null);
Task<List<TaskAgent>> GetAgentsAsync(string agentName);
Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent); Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent);
// messagequeue // messagequeue
@@ -49,7 +51,7 @@ namespace GitHub.Runner.Common
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken); Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
// agent update // agent update
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState); Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace);
} }
public sealed class RunnerServer : RunnerService, IRunnerServer public sealed class RunnerServer : RunnerService, IRunnerServer
@@ -252,6 +254,11 @@ namespace GitHub.Runner.Common
return _genericTaskAgentClient.GetAgentsAsync(agentPoolId, agentName, false); return _genericTaskAgentClient.GetAgentsAsync(agentPoolId, agentName, false);
} }
public Task<List<TaskAgent>> GetAgentsAsync(string agentName)
{
return GetAgentsAsync(0, agentName); // search in all all agentPools
}
public Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent) public Task<TaskAgent> ReplaceAgentAsync(int agentPoolId, TaskAgent agent)
{ {
CheckConnection(RunnerConnectionType.Generic); CheckConnection(RunnerConnectionType.Generic);
@@ -264,6 +271,11 @@ namespace GitHub.Runner.Common
return _genericTaskAgentClient.DeleteAgentAsync(agentPoolId, agentId); return _genericTaskAgentClient.DeleteAgentAsync(agentPoolId, agentId);
} }
public Task DeleteAgentAsync(int agentId)
{
return DeleteAgentAsync(0, agentId); // agentPool is ignored server side
}
//----------------------------------------------------------------- //-----------------------------------------------------------------
// MessageQueue // MessageQueue
//----------------------------------------------------------------- //-----------------------------------------------------------------
@@ -329,25 +341,10 @@ namespace GitHub.Runner.Common
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken); return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
} }
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState) public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace)
{ {
CheckConnection(RunnerConnectionType.Generic); CheckConnection(RunnerConnectionType.Generic);
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState); return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState, trace);
}
//-----------------------------------------------------------------
// Runner Auth Url
//-----------------------------------------------------------------
public Task<string> GetRunnerAuthUrlAsync(int runnerPoolId, int runnerId)
{
CheckConnection(RunnerConnectionType.MessageQueue);
return _messageTaskAgentClient.GetAgentAuthUrlAsync(runnerPoolId, runnerId);
}
public Task ReportRunnerAuthUrlErrorAsync(int runnerPoolId, int runnerId, string error)
{
CheckConnection(RunnerConnectionType.MessageQueue);
return _messageTaskAgentClient.ReportAgentAuthUrlMigrationErrorAsync(runnerPoolId, runnerId, error);
} }
} }
} }

View File

@@ -164,9 +164,8 @@ namespace GitHub.Runner.Common
if (!Silent) if (!Silent)
{ {
Console.WriteLine(); Console.WriteLine();
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine($"# {message}");
Console.ResetColor(); Console.ResetColor();
Console.WriteLine($"# {message}");
Console.WriteLine(); Console.WriteLine();
} }
} }
@@ -177,9 +176,8 @@ namespace GitHub.Runner.Common
{ {
Console.ForegroundColor = ConsoleColor.Green; Console.ForegroundColor = ConsoleColor.Green;
Console.Write("√ "); Console.Write("√ ");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(message);
Console.ResetColor(); Console.ResetColor();
Console.WriteLine(message);
} }
} }

View File

@@ -29,10 +29,11 @@ namespace GitHub.Runner.Listener
{ {
Constants.Runner.CommandLine.Flags.Check, Constants.Runner.CommandLine.Flags.Check,
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.Once,
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
}; };
@@ -66,7 +67,9 @@ 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);
// Keep this around since customers still relies on it
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once); public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
// Constructor. // Constructor.
@@ -240,6 +243,7 @@ namespace GitHub.Runner.Listener
validator: Validators.ServerUrlValidator); validator: Validators.ServerUrlValidator);
} }
#if OS_WINDOWS
public string GetWindowsLogonAccount(string defaultValue, string descriptionMsg) public string GetWindowsLogonAccount(string defaultValue, string descriptionMsg)
{ {
return GetArgOrPrompt( return GetArgOrPrompt(
@@ -257,7 +261,7 @@ namespace GitHub.Runner.Listener
defaultValue: string.Empty, defaultValue: string.Empty,
validator: Validators.NonEmptyValidator); validator: Validators.NonEmptyValidator);
} }
#endif
public string GetWork() public string GetWork()
{ {
return GetArgOrPrompt( return GetArgOrPrompt(

View File

@@ -22,6 +22,7 @@ namespace GitHub.Runner.Listener.Configuration
bool IsConfigured(); bool IsConfigured();
Task ConfigureAsync(CommandSettings command); Task ConfigureAsync(CommandSettings command);
Task UnconfigureAsync(CommandSettings command); Task UnconfigureAsync(CommandSettings command);
void DeleteLocalRunnerConfig();
RunnerSettings LoadSettings(); RunnerSettings LoadSettings();
} }
@@ -53,7 +54,7 @@ namespace GitHub.Runner.Listener.Configuration
Trace.Info(nameof(LoadSettings)); Trace.Info(nameof(LoadSettings));
if (!IsConfigured()) if (!IsConfigured())
{ {
throw new InvalidOperationException("Not configured"); throw new InvalidOperationException("Not configured. Run config.(sh/cmd) to configure the runner.");
} }
RunnerSettings settings = _store.GetSettings(); RunnerSettings settings = _store.GetSettings();
@@ -65,18 +66,18 @@ namespace GitHub.Runner.Listener.Configuration
public async Task ConfigureAsync(CommandSettings command) public async Task ConfigureAsync(CommandSettings command)
{ {
_term.WriteLine(); _term.WriteLine();
_term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); _term.WriteLine("--------------------------------------------------------------------------------");
_term.WriteLine("| ____ _ _ _ _ _ _ _ _ |", ConsoleColor.White); _term.WriteLine("| ____ _ _ _ _ _ _ _ _ |");
_term.WriteLine("| / ___(_) |_| | | |_ _| |__ / \\ ___| |_(_) ___ _ __ ___ |", ConsoleColor.White); _term.WriteLine("| / ___(_) |_| | | |_ _| |__ / \\ ___| |_(_) ___ _ __ ___ |");
_term.WriteLine("| | | _| | __| |_| | | | | '_ \\ / _ \\ / __| __| |/ _ \\| '_ \\/ __| |", ConsoleColor.White); _term.WriteLine("| | | _| | __| |_| | | | | '_ \\ / _ \\ / __| __| |/ _ \\| '_ \\/ __| |");
_term.WriteLine("| | |_| | | |_| _ | |_| | |_) | / ___ \\ (__| |_| | (_) | | | \\__ \\ |", ConsoleColor.White); _term.WriteLine("| | |_| | | |_| _ | |_| | |_) | / ___ \\ (__| |_| | (_) | | | \\__ \\ |");
_term.WriteLine("| \\____|_|\\__|_| |_|\\__,_|_.__/ /_/ \\_\\___|\\__|_|\\___/|_| |_|___/ |", ConsoleColor.White); _term.WriteLine("| \\____|_|\\__|_| |_|\\__,_|_.__/ /_/ \\_\\___|\\__|_|\\___/|_| |_|___/ |");
_term.WriteLine("| |", ConsoleColor.White); _term.WriteLine("| |");
_term.Write("| ", ConsoleColor.White); _term.Write("| ");
_term.Write("Self-hosted runner registration", ConsoleColor.Cyan); _term.Write("Self-hosted runner registration", ConsoleColor.Cyan);
_term.WriteLine(" |", ConsoleColor.White); _term.WriteLine(" |");
_term.WriteLine("| |", ConsoleColor.White); _term.WriteLine("| |");
_term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); _term.WriteLine("--------------------------------------------------------------------------------");
Trace.Info(nameof(ConfigureAsync)); Trace.Info(nameof(ConfigureAsync));
if (IsConfigured()) if (IsConfigured())
@@ -117,6 +118,7 @@ namespace GitHub.Runner.Listener.Configuration
try try
{ {
// Determine the service deployment type based on connection data. (Hosted/OnPremises) // Determine the service deployment type based on connection data. (Hosted/OnPremises)
// Hosted usually means github.com or localhost, while OnPremises means GHES or GHAE
runnerSettings.IsHostedServer = runnerSettings.GitHubUrl == null || UrlUtil.IsHostedServer(new UriBuilder(runnerSettings.GitHubUrl)); runnerSettings.IsHostedServer = runnerSettings.GitHubUrl == null || UrlUtil.IsHostedServer(new UriBuilder(runnerSettings.GitHubUrl));
// Warn if the Actions server url and GHES server url has different Host // Warn if the Actions server url and GHES server url has different Host
@@ -165,7 +167,7 @@ namespace GitHub.Runner.Listener.Configuration
List<TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync(); List<TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync();
TaskAgentPool defaultPool = agentPools?.Where(x => x.IsInternal).FirstOrDefault(); TaskAgentPool defaultPool = agentPools?.Where(x => x.IsInternal).FirstOrDefault();
if (agentPools?.Where(x => !x.IsHosted).Count() > 1) if (agentPools?.Where(x => !x.IsHosted).Count() > 0)
{ {
poolName = command.GetRunnerGroupName(defaultPool?.Name); poolName = command.GetRunnerGroupName(defaultPool?.Name);
_term.WriteLine(); _term.WriteLine();
@@ -186,7 +188,7 @@ namespace GitHub.Runner.Listener.Configuration
} }
else else
{ {
Trace.Info("Found a self-hosted runner group with id {1} and name {2}", agentPool.Id, agentPool.Name); Trace.Info($"Found a self-hosted runner group with id {agentPool.Id} and name {agentPool.Name}");
runnerSettings.PoolId = agentPool.Id; runnerSettings.PoolId = agentPool.Id;
runnerSettings.PoolName = agentPool.Name; runnerSettings.PoolName = agentPool.Name;
} }
@@ -194,6 +196,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();
@@ -210,7 +213,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
{ {
@@ -233,7 +236,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
{ {
@@ -327,6 +330,38 @@ namespace GitHub.Runner.Listener.Configuration
#endif #endif
} }
// Delete .runner and .credentials files
public void DeleteLocalRunnerConfig()
{
bool isConfigured = _store.IsConfigured();
bool hasCredentials = _store.HasCredentials();
//delete credential config files
var currentAction = "Removing .credentials";
if (hasCredentials)
{
_store.DeleteCredential();
var keyManager = HostContext.GetService<IRSAKeyManager>();
keyManager.DeleteKey();
_term.WriteSuccessMessage("Removed .credentials");
}
else
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
//delete settings config file
currentAction = "Removing .runner";
if (isConfigured)
{
_store.DeleteSettings();
_term.WriteSuccessMessage("Removed .runner");
}
else
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
}
public async Task UnconfigureAsync(CommandSettings command) public async Task UnconfigureAsync(CommandSettings command)
{ {
string currentAction = string.Empty; string currentAction = string.Empty;
@@ -346,12 +381,9 @@ namespace GitHub.Runner.Listener.Configuration
_term.WriteLine(); _term.WriteLine();
_term.WriteSuccessMessage("Runner service removed"); _term.WriteSuccessMessage("Runner service removed");
#elif OS_LINUX #else
// unconfig system D service first // unconfig systemd or osx service first
throw new Exception("Unconfigure service first"); throw new Exception("Uninstall service first");
#elif OS_OSX
// unconfig osx service first
throw new Exception("Unconfigure service first");
#endif #endif
} }
@@ -383,7 +415,7 @@ namespace GitHub.Runner.Listener.Configuration
// Determine the service deployment type based on connection data. (Hosted/OnPremises) // Determine the service deployment type based on connection data. (Hosted/OnPremises)
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds); await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
var agents = await _runnerServer.GetAgentsAsync(settings.PoolId, settings.AgentName); var agents = await _runnerServer.GetAgentsAsync(settings.AgentName);
Trace.Verbose("Returns {0} agents", agents.Count); Trace.Verbose("Returns {0} agents", agents.Count);
TaskAgent agent = agents.FirstOrDefault(); TaskAgent agent = agents.FirstOrDefault();
if (agent == null) if (agent == null)
@@ -392,7 +424,7 @@ namespace GitHub.Runner.Listener.Configuration
} }
else else
{ {
await _runnerServer.DeleteAgentAsync(settings.PoolId, settings.AgentId); await _runnerServer.DeleteAgentAsync(settings.AgentId);
_term.WriteLine(); _term.WriteLine();
_term.WriteSuccessMessage("Runner removed successfully"); _term.WriteSuccessMessage("Runner removed successfully");
@@ -403,31 +435,7 @@ namespace GitHub.Runner.Listener.Configuration
_term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server."); _term.WriteLine("Cannot connect to server, because config files are missing. Skipping removing runner from the server.");
} }
//delete credential config files DeleteLocalRunnerConfig();
currentAction = "Removing .credentials";
if (hasCredentials)
{
_store.DeleteCredential();
var keyManager = HostContext.GetService<IRSAKeyManager>();
keyManager.DeleteKey();
_term.WriteSuccessMessage("Removed .credentials");
}
else
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
//delete settings config file
currentAction = "Removing .runner";
if (isConfigured)
{
_store.DeleteSettings();
_term.WriteSuccessMessage("Removed .runner");
}
else
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
} }
catch (Exception) catch (Exception)
{ {
@@ -458,7 +466,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
@@ -469,6 +477,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();
@@ -484,7 +494,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)
{ {
@@ -495,6 +505,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));

View File

@@ -1,4 +1,5 @@
#if OS_WINDOWS #if OS_WINDOWS
#pragma warning disable CA1416
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@@ -87,7 +88,7 @@ namespace GitHub.Runner.Listener.Configuration
public string GetUniqueRunnerGroupName() public string GetUniqueRunnerGroupName()
{ {
return RunnerServiceLocalGroupPrefix + IOUtil.GetPathHash(HostContext.GetDirectory(WellKnownDirectory.Bin)).Substring(0, 5); return RunnerServiceLocalGroupPrefix + IOUtil.GetSha256Hash(HostContext.GetDirectory(WellKnownDirectory.Bin)).Substring(0, 5);
} }
public bool LocalGroupExists(string groupName) public bool LocalGroupExists(string groupName)
@@ -1327,4 +1328,5 @@ namespace GitHub.Runner.Listener.Configuration
public IntPtr hProfile; public IntPtr hProfile;
} }
} }
#pragma warning restore CA1416
#endif #endif

View File

@@ -85,7 +85,7 @@ namespace GitHub.Runner.Listener.Configuration
while (true) while (true)
{ {
// Write the message prompt. // Write the message prompt.
_terminal.Write($"{description} ", ConsoleColor.White); _terminal.Write($"{description} ");
if(!string.IsNullOrEmpty(defaultValue)) if(!string.IsNullOrEmpty(defaultValue))
{ {

View File

@@ -67,6 +67,8 @@ namespace GitHub.Runner.Listener.Configuration
return !string.IsNullOrEmpty(value); return !string.IsNullOrEmpty(value);
} }
#if OS_WINDOWS
#pragma warning disable CA1416
public static bool NTAccountValidator(string arg) public static bool NTAccountValidator(string arg)
{ {
if (string.IsNullOrEmpty(arg) || String.IsNullOrEmpty(arg.TrimStart('.', '\\'))) if (string.IsNullOrEmpty(arg) || String.IsNullOrEmpty(arg.TrimStart('.', '\\')))
@@ -87,5 +89,7 @@ namespace GitHub.Runner.Listener.Configuration
return true; return true;
} }
#pragma warning restore CA1416
#endif
} }
} }

View File

@@ -1,4 +1,5 @@
#if OS_WINDOWS #if OS_WINDOWS
#pragma warning disable CA1416
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -169,4 +170,5 @@ namespace GitHub.Runner.Listener.Configuration
} }
} }
} }
#pragma warning restore CA1416
#endif #endif

View File

@@ -27,24 +27,27 @@ namespace GitHub.Runner.Listener
Task ShutdownAsync(); Task ShutdownAsync();
} }
// This implementation of IDobDispatcher is not thread safe. // This implementation of IJobDispatcher is not thread safe.
// It is base on the fact that the current design of runner is dequeue // It is based on the fact that the current design of the runner is a dequeue
// and process one message from message queue everytime. // and processes one message from the message queue at a time.
// In addition, it only execute one job every time, // In addition, it only executes one job every time,
// and server will not send another job while this one is still running. // and the server will not send another job while this one is still running.
public sealed class JobDispatcher : RunnerService, IJobDispatcher public sealed class JobDispatcher : RunnerService, IJobDispatcher
{ {
private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>(); private readonly Lazy<Dictionary<long, TaskResult>> _localRunJobResult = new Lazy<Dictionary<long, TaskResult>>();
private int _poolId; private int _poolId;
RunnerSettings _runnerSetting;
IConfigurationStore _configurationStore;
RunnerSettings _runnerSettings;
private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}"; private static readonly string _workerProcessName = $"Runner.Worker{IOUtil.ExeExtension}";
// this is not thread-safe // this is not thread-safe
private readonly Queue<Guid> _jobDispatchedQueue = new Queue<Guid>(); private readonly Queue<Guid> _jobDispatchedQueue = new Queue<Guid>();
private readonly ConcurrentDictionary<Guid, WorkerDispatcher> _jobInfos = new ConcurrentDictionary<Guid, WorkerDispatcher>(); private readonly ConcurrentDictionary<Guid, WorkerDispatcher> _jobInfos = new ConcurrentDictionary<Guid, WorkerDispatcher>();
//allow up to 30sec for any data to be transmitted over the process channel // allow up to 30sec for any data to be transmitted over the process channel
//timeout limit can be overwrite by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT // timeout limit can be overwritten by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT
private TimeSpan _channelTimeout; private TimeSpan _channelTimeout;
private TaskCompletionSource<bool> _runOnceJobCompleted = new TaskCompletionSource<bool>(); private TaskCompletionSource<bool> _runOnceJobCompleted = new TaskCompletionSource<bool>();
@@ -54,9 +57,9 @@ namespace GitHub.Runner.Listener
base.Initialize(hostContext); base.Initialize(hostContext);
// get pool id from config // get pool id from config
var configurationStore = hostContext.GetService<IConfigurationStore>(); _configurationStore = hostContext.GetService<IConfigurationStore>();
_runnerSetting = configurationStore.GetSettings(); _runnerSettings = _configurationStore.GetSettings();
_poolId = _runnerSetting.PoolId; _poolId = _runnerSettings.PoolId;
int channelTimeoutSeconds; int channelTimeoutSeconds;
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds)) if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT") ?? string.Empty, out channelTimeoutSeconds))
@@ -64,7 +67,7 @@ namespace GitHub.Runner.Listener
channelTimeoutSeconds = 30; channelTimeoutSeconds = 30;
} }
// _channelTimeout should in range [30, 300] seconds // _channelTimeout should be in range [30, 300] seconds
_channelTimeout = TimeSpan.FromSeconds(Math.Min(Math.Max(channelTimeoutSeconds, 30), 300)); _channelTimeout = TimeSpan.FromSeconds(Math.Min(Math.Max(channelTimeoutSeconds, 30), 300));
Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds."); Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds.");
} }
@@ -230,16 +233,27 @@ namespace GitHub.Runner.Listener
return; return;
} }
// base on the current design, server will only send one job for a given runner everytime. // based on the current design, server will only send one job for a given runner at a time.
// if the runner received a new job request while a previous job request is still running, this typically indicate two situations // if the runner received a new job request while a previous job request is still running, this typically indicates two situations
// 1. an runner bug cause server and runner mismatch on the state of the job request, ex. runner not renew jobrequest properly but think it still own the job reqest, however server already abandon the jobrequest. // 1. a runner bug caused a server and runner mismatch on the state of the job request, e.g. the runner didn't renew the jobrequest
// 2. a server bug or design change that allow server send more than one job request to an given runner that haven't finish previous job request. // properly but thinks it still owns the job reqest, however the server has already abandoned the jobrequest.
// 2. a server bug or design change that allowed the server to send more than one job request to an given runner that hasn't finished
//. a previous job request.
var runnerServer = HostContext.GetService<IRunnerServer>(); var runnerServer = HostContext.GetService<IRunnerServer>();
TaskAgentJobRequest request = null; TaskAgentJobRequest request = null;
try try
{ {
request = await runnerServer.GetAgentRequestAsync(_poolId, jobDispatch.RequestId, CancellationToken.None); request = await runnerServer.GetAgentRequestAsync(_poolId, jobDispatch.RequestId, CancellationToken.None);
} }
catch (TaskAgentJobNotFoundException ex)
{
Trace.Error($"Catch job-not-found exception while checking jobrequest {jobDispatch.JobId} status. Cancel running worker right away.");
Trace.Error(ex);
jobDispatch.WorkerCancellationTokenSource.Cancel();
// make sure worker process exits before we return, otherwise we might leave an orphan worker process behind.
await jobDispatch.WorkerDispatch;
return;
}
catch (Exception ex) catch (Exception ex)
{ {
// we can't even query for the jobrequest from server, something totally busted, stop runner/worker. // we can't even query for the jobrequest from server, something totally busted, stop runner/worker.
@@ -247,7 +261,7 @@ namespace GitHub.Runner.Listener
Trace.Error(ex); Trace.Error(ex);
jobDispatch.WorkerCancellationTokenSource.Cancel(); jobDispatch.WorkerCancellationTokenSource.Cancel();
// make sure worker process exit before we rethrow, otherwise we might leave orphan worker process behind. // make sure the worker process exits before we rethrow, otherwise we might leave orphan worker process behind.
await jobDispatch.WorkerDispatch; await jobDispatch.WorkerDispatch;
// rethrow original exception // rethrow original exception
@@ -256,8 +270,8 @@ namespace GitHub.Runner.Listener
if (request.Result != null) if (request.Result != null)
{ {
// job request has been finished, the server already has result. // job request has been finished, the server already has the result.
// this means runner is busted since it still running that request. // this means the runner is busted since it is still running that request.
// cancel the zombie worker, run next job request. // cancel the zombie worker, run next job request.
Trace.Error($"Received job request while previous job {jobDispatch.JobId} still running on worker. Cancel the previous job since the job request have been finished on server side with result: {request.Result.Value}."); Trace.Error($"Received job request while previous job {jobDispatch.JobId} still running on worker. Cancel the previous job since the job request have been finished on server side with result: {request.Result.Value}.");
jobDispatch.WorkerCancellationTokenSource.Cancel(); jobDispatch.WorkerCancellationTokenSource.Cancel();
@@ -496,7 +510,20 @@ namespace GitHub.Runner.Listener
{ {
detailInfo = string.Join(Environment.NewLine, workerOutput); detailInfo = string.Join(Environment.NewLine, workerOutput);
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result."); Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
await LogWorkerProcessUnhandledException(message, detailInfo);
var jobServer = HostContext.GetService<IJobServer>();
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
await jobServer.ConnectAsync(jobConnection);
await LogWorkerProcessUnhandledException(jobServer, message, detailInfo);
// Go ahead to finish the job with result 'Failed' if the STDERR from worker is System.IO.IOException, since it typically means we are running out of disk space.
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
{
Trace.Info($"Finish job with result 'Failed' due to IOException.");
await ForceFailJob(jobServer, message);
}
} }
TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode); TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode);
@@ -637,13 +664,15 @@ namespace GitHub.Runner.Listener
try try
{ {
request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token); request = await runnerServer.RenewAgentRequestAsync(poolId, requestId, lockToken, orchestrationId, token);
Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}"); Trace.Info($"Successfully renew job request {requestId}, job is valid till {request.LockedUntil.Value}");
if (!firstJobRequestRenewed.Task.IsCompleted) if (!firstJobRequestRenewed.Task.IsCompleted)
{ {
// fire first renew succeed event. // fire first renew succeed event.
firstJobRequestRenewed.TrySetResult(0); firstJobRequestRenewed.TrySetResult(0);
// Update settings if the runner name has been changed server-side
UpdateAgentNameIfNeeded(request.ReservedAgent?.Name);
} }
if (encounteringError > 0) if (encounteringError > 0)
@@ -743,6 +772,27 @@ namespace GitHub.Runner.Listener
} }
} }
private void UpdateAgentNameIfNeeded(string agentName)
{
var isNewAgentName = !string.Equals(_runnerSettings.AgentName, agentName, StringComparison.Ordinal);
if (!isNewAgentName || string.IsNullOrEmpty(agentName))
{
return;
}
_runnerSettings.AgentName = agentName;
try
{
_configurationStore.SaveSettings(_runnerSettings);
}
catch (Exception ex)
{
Trace.Error("Cannot update the settings file:");
Trace.Error(ex);
}
}
// Best effort upload any logs for this job. // Best effort upload any logs for this job.
private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message) private async Task TryUploadUnfinishedLogs(Pipelines.AgentJobRequestMessage message)
{ {
@@ -904,53 +954,16 @@ namespace GitHub.Runner.Listener
} }
// log an error issue to job level timeline record // log an error issue to job level timeline record
private async Task LogWorkerProcessUnhandledException(Pipelines.AgentJobRequestMessage message, string errorMessage) private async Task LogWorkerProcessUnhandledException(IJobServer jobServer, Pipelines.AgentJobRequestMessage message, string errorMessage)
{ {
try try
{ {
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection));
ArgUtil.NotNull(systemConnection, nameof(systemConnection));
var jobServer = HostContext.GetService<IJobServer>();
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
/* Below is the legacy 'OnPremises' code that is currently unused by the runner
ToDo: re-implement code as appropriate once GHES support is added.
// Make sure SystemConnection Url match Config Url base for OnPremises server
if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType) ||
string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase))
{
try
{
Uri result = null;
Uri configUri = new Uri(_runnerSetting.ServerUrl);
if (Uri.TryCreate(new Uri(configUri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)), jobServerUrl.PathAndQuery, out result))
{
//replace the schema and host portion of messageUri with the host from the
//server URI (which was set at config time)
jobServerUrl = result;
}
}
catch (InvalidOperationException ex)
{
//cannot parse the Uri - not a fatal error
Trace.Error(ex);
}
catch (UriFormatException ex)
{
//cannot parse the Uri - not a fatal error
Trace.Error(ex);
}
} */
await jobServer.ConnectAsync(jobConnection);
var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None); var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None);
ArgUtil.NotNull(timeline, nameof(timeline)); ArgUtil.NotNull(timeline, nameof(timeline));
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job"); TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
ArgUtil.NotNull(jobRecord, nameof(jobRecord)); ArgUtil.NotNull(jobRecord, nameof(jobRecord));
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage }; var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage };
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash; unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
jobRecord.ErrorCount++; jobRecord.ErrorCount++;
@@ -964,6 +977,21 @@ namespace GitHub.Runner.Listener
} }
} }
// raise job completed event to fail the job.
private async Task ForceFailJob(IJobServer jobServer, Pipelines.AgentJobRequestMessage message)
{
try
{
var jobCompletedEvent = new JobCompletedEvent(message.RequestId, message.JobId, TaskResult.Failed);
await jobServer.RaisePlanEventAsync<JobCompletedEvent>(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, jobCompletedEvent, CancellationToken.None);
}
catch (Exception ex)
{
Trace.Error("Fail to raise JobCompletedEvent back to service.");
Trace.Error(ex);
}
}
private class WorkerDispatcher : IDisposable private class WorkerDispatcher : IDisposable
{ {
public long RequestId { get; } public long RequestId { get; }

View File

@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit> <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -26,6 +24,12 @@
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\Misc\runnercoreassets">
<LogicalName>GitHub.Runner.Listener.runnercoreassets</LogicalName>
</EmbeddedResource>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@@ -214,7 +214,7 @@ namespace GitHub.Runner.Listener
var startupTypeAsString = command.GetStartupType(); var startupTypeAsString = command.GetStartupType();
if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService) if (string.IsNullOrEmpty(startupTypeAsString) && configuredAsService)
{ {
// We need try our best to make the startup type accurate // We need try our best to make the startup type accurate
// The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary // The problem is coming from runner autoupgrade, which result an old version service host binary but a newer version runner binary
// At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service. // At that time the servicehost won't pass --startuptype to Runner.Listener while the runner is actually running as service.
// We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not // We will guess the startup type only when the runner is configured as service and the guess will based on whether STDOUT/STDERR/STDIN been redirect or not
@@ -233,8 +233,14 @@ namespace GitHub.Runner.Listener
Trace.Info($"Set runner startup type - {startType}"); Trace.Info($"Set runner startup type - {startType}");
HostContext.StartupType = startType; HostContext.StartupType = startType;
if (command.RunOnce)
{
_term.WriteLine("Warning: '--once' is going to be deprecated in the future, please consider using '--ephemeral' during runner registration.", ConsoleColor.Yellow);
_term.WriteLine("https://docs.github.com/en/actions/hosting-your-own-runners/autoscaling-with-self-hosted-runners#using-ephemeral-runners-for-autoscaling", ConsoleColor.Yellow);
}
// Run the runner interactively or as service // Run the runner interactively or as service
return await RunAsync(settings, command.RunOnce); return await RunAsync(settings, command.RunOnce || settings.Ephemeral);
} }
else else
{ {
@@ -306,10 +312,15 @@ namespace GitHub.Runner.Listener
} }
HostContext.WritePerfCounter("SessionCreated"); HostContext.WritePerfCounter("SessionCreated");
_term.WriteLine($"Current runner version: '{BuildConstants.RunnerPackage.Version}'");
_term.WriteLine($"{DateTime.UtcNow:u}: Listening for Jobs"); _term.WriteLine($"{DateTime.UtcNow:u}: Listening for Jobs");
IJobDispatcher jobDispatcher = null; IJobDispatcher jobDispatcher = null;
CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken); CancellationTokenSource messageQueueLoopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(HostContext.RunnerShutdownToken);
// Should we try to cleanup ephemeral runners
bool runOnceJobCompleted = false;
try try
{ {
var notification = HostContext.GetService<IJobNotification>(); var notification = HostContext.GetService<IJobNotification>();
@@ -371,6 +382,7 @@ namespace GitHub.Runner.Listener
Task completeTask = await Task.WhenAny(getNextMessage, jobDispatcher.RunOnceJobCompleted.Task); Task completeTask = await Task.WhenAny(getNextMessage, jobDispatcher.RunOnceJobCompleted.Task);
if (completeTask == jobDispatcher.RunOnceJobCompleted.Task) if (completeTask == jobDispatcher.RunOnceJobCompleted.Task)
{ {
runOnceJobCompleted = true;
Trace.Info("Job has finished at backend, the runner will exit since it is running under onetime use mode."); Trace.Info("Job has finished at backend, the runner will exit since it is running under onetime use mode.");
Trace.Info("Stop message queue looping."); Trace.Info("Stop message queue looping.");
messageQueueLoopTokenSource.Cancel(); messageQueueLoopTokenSource.Cancel();
@@ -466,10 +478,24 @@ namespace GitHub.Runner.Listener
await jobDispatcher.ShutdownAsync(); await jobDispatcher.ShutdownAsync();
} }
//TODO: make sure we don't mask more important exception try
await _listener.DeleteSessionAsync(); {
await _listener.DeleteSessionAsync();
}
catch (Exception ex) when (runOnce)
{
// ignore exception during delete session for ephemeral runner since the runner might already be deleted from the server side
// and the delete session call will ends up with 401.
Trace.Info($"Ignore any exception during DeleteSession for an ephemeral runner. {ex}");
}
messageQueueLoopTokenSource.Dispose(); messageQueueLoopTokenSource.Dispose();
if (settings.Ephemeral && runOnceJobCompleted)
{
var configManager = HostContext.GetService<IConfigurationManager>();
configManager.DeleteLocalRunnerConfig();
}
} }
} }
catch (TaskAgentAccessTokenExpiredException) catch (TaskAgentAccessTokenExpiredException)
@@ -512,7 +538,9 @@ Config Options:
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}' --labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
--work string Relative runner work directory (default {Constants.Path.WorkDirectory}) --work string Relative runner work directory (default {Constants.Path.WorkDirectory})
--replace Replace any existing runner with the same name (default false) --replace Replace any existing runner with the same name (default false)
--pat GitHub personal access token used for checking network connectivity when executing `.{separator}run.{ext} --check`"); --pat GitHub personal access token used for checking network connectivity when executing `.{separator}run.{ext} --check`
--ephemeral Configure the runner to only take one job and then let the service un-configure the runner after the job finishes (default false)");
#if OS_WINDOWS #if OS_WINDOWS
_term.WriteLine($@" --runasservice Run the runner as a service"); _term.WriteLine($@" --runasservice Run the runner as a service");
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice"); _term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");

View File

@@ -13,6 +13,9 @@ using GitHub.Services.WebApi;
using GitHub.Services.Common; using GitHub.Services.Common;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using System.Text;
using System.Collections.Generic;
using System.Reflection;
namespace GitHub.Runner.Listener namespace GitHub.Runner.Listener
{ {
@@ -33,6 +36,7 @@ namespace GitHub.Runner.Listener
private IRunnerServer _runnerServer; private IRunnerServer _runnerServer;
private int _poolId; private int _poolId;
private int _agentId; private int _agentId;
private readonly List<string> _updateTrace = new List<string>();
public bool Busy { get; private set; } public bool Busy { get; private set; }
@@ -53,6 +57,8 @@ namespace GitHub.Runner.Listener
Busy = true; Busy = true;
try try
{ {
var totalUpdateTime = Stopwatch.StartNew();
if (!await UpdateNeeded(updateMessage.TargetVersion, token)) if (!await UpdateNeeded(updateMessage.TargetVersion, token))
{ {
Trace.Info($"Can't find available update package."); Trace.Info($"Can't find available update package.");
@@ -60,6 +66,7 @@ namespace GitHub.Runner.Listener
} }
Trace.Info($"An update is available."); Trace.Info($"An update is available.");
_updateTrace.Add($"RunnerPlatform: {_targetPackage.Platform}");
// 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.");
@@ -74,11 +81,14 @@ namespace GitHub.Runner.Listener
await jobDispatcher.WaitAsync(token); await jobDispatcher.WaitAsync(token);
Trace.Info($"All running job has exited."); Trace.Info($"All running job has exited.");
// We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743
// delete runner backup // delete runner backup
var stopWatch = Stopwatch.StartNew();
DeletePreviousVersionRunnerBackup(token); DeletePreviousVersionRunnerBackup(token);
Trace.Info($"Delete old version runner backup."); Trace.Info($"Delete old version runner backup.");
stopWatch.Stop();
// generate update script from template // generate update script from template
_updateTrace.Add($"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms");
await UpdateRunnerUpdateStateAsync("Generate and execute update script."); await UpdateRunnerUpdateStateAsync("Generate and execute update script.");
string updateScript = GenerateUpdateScript(restartInteractiveRunner); string updateScript = GenerateUpdateScript(restartInteractiveRunner);
@@ -96,12 +106,21 @@ namespace GitHub.Runner.Listener
invokeScript.Start(); invokeScript.Start();
Trace.Info($"Update script start running"); Trace.Info($"Update script start running");
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should back online within 10 seconds."); totalUpdateTime.Stop();
_updateTrace.Add($"TotalUpdateTime: {totalUpdateTime.ElapsedMilliseconds}ms");
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds.");
return true; return true;
} }
catch (Exception ex)
{
_updateTrace.Add(ex.ToString());
throw;
}
finally finally
{ {
await UpdateRunnerUpdateStateAsync("Runner update process finished.");
Busy = false; Busy = false;
} }
} }
@@ -156,171 +175,23 @@ namespace GitHub.Runner.Listener
IOUtil.DeleteDirectory(latestRunnerDirectory, token); IOUtil.DeleteDirectory(latestRunnerDirectory, token);
Directory.CreateDirectory(latestRunnerDirectory); Directory.CreateDirectory(latestRunnerDirectory);
int runnerSuffix = 1;
string archiveFile = null; string archiveFile = null;
bool downloadSucceeded = false; var packageDownloadUrl = _targetPackage.DownloadUrl;
var packageHashValue = _targetPackage.HashValue;
_updateTrace.Add($"DownloadUrl: {packageDownloadUrl}");
try try
{ {
// Download the runner, using multiple attempts in order to be resilient against any networking/CDN issues archiveFile = await DownLoadRunner(latestRunnerDirectory, packageDownloadUrl, packageHashValue, token);
for (int attempt = 1; attempt <= Constants.RunnerDownloadRetryMaxAttempts; attempt++)
if (string.IsNullOrEmpty(archiveFile))
{ {
// Generate an available package name, and do our best effort to clean up stale local zip files throw new TaskCanceledException($"Runner package '{packageDownloadUrl}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts");
while (true)
{
if (_targetPackage.Platform.StartsWith("win"))
{
archiveFile = Path.Combine(latestRunnerDirectory, $"runner{runnerSuffix}.zip");
}
else
{
archiveFile = Path.Combine(latestRunnerDirectory, $"runner{runnerSuffix}.tar.gz");
}
try
{
// delete .zip file
if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile))
{
Trace.Verbose("Deleting latest runner package zip '{0}'", archiveFile);
IOUtil.DeleteFile(archiveFile);
}
break;
}
catch (Exception ex)
{
// couldn't delete the file for whatever reason, so generate another name
Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex);
runnerSuffix++;
}
}
// Allow a 15-minute package download timeout, which is good enough to update the runner from a 1 Mbit/s ADSL connection.
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_DOWNLOAD_TIMEOUT") ?? string.Empty, out int timeoutSeconds))
{
timeoutSeconds = 15 * 60;
}
Trace.Info($"Attempt {attempt}: save latest runner into {archiveFile}.");
using (var downloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
using (var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(downloadTimeout.Token, token))
{
try
{
Trace.Info($"Download runner: begin download");
//open zip stream in async mode
using (HttpClient httpClient = new HttpClient(HostContext.CreateHttpClientHandler()))
{
if (!string.IsNullOrEmpty(_targetPackage.Token))
{
Trace.Info($"Adding authorization token ({_targetPackage.Token.Length} chars)");
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _targetPackage.Token);
}
Trace.Info($"Downloading {_targetPackage.DownloadUrl}");
using (FileStream fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (Stream result = await httpClient.GetStreamAsync(_targetPackage.DownloadUrl))
{
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
await result.CopyToAsync(fs, 81920, downloadCts.Token);
await fs.FlushAsync(downloadCts.Token);
}
}
Trace.Info($"Download runner: finished download");
downloadSucceeded = true;
break;
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
Trace.Info($"Runner download has been canceled.");
throw;
}
catch (Exception ex)
{
if (downloadCts.Token.IsCancellationRequested)
{
Trace.Warning($"Runner download has timed out after {timeoutSeconds} seconds");
}
Trace.Warning($"Failed to get package '{archiveFile}' from '{_targetPackage.DownloadUrl}'. Exception {ex}");
}
}
} }
if (!downloadSucceeded) await ValidateRunnerHash(archiveFile, packageHashValue);
{
throw new TaskCanceledException($"Runner package '{archiveFile}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts");
}
// If we got this far, we know that we've successfully downloaded the runner package await ExtractRunnerPackage(archiveFile, latestRunnerDirectory, token);
// Validate Hash Matches if it is provided
using (FileStream stream = File.OpenRead(archiveFile))
{
if (!String.IsNullOrEmpty(_targetPackage.HashValue))
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] srcHashBytes = await sha256.ComputeHashAsync(stream);
var hash = PrimitiveExtensions.ConvertToHexString(srcHashBytes);
if (hash != _targetPackage.HashValue)
{
// Hash did not match, we can't recover from this, just throw
throw new Exception($"Computed runner hash {hash} did not match expected Runner Hash {_targetPackage.HashValue} for {_targetPackage.Filename}");
}
Trace.Info($"Validated Runner Hash matches {_targetPackage.Filename} : {_targetPackage.HashValue}");
}
}
}
if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
ZipFile.ExtractToDirectory(archiveFile, latestRunnerDirectory);
}
else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
{
string tar = WhichUtil.Which("tar", trace: Trace);
if (string.IsNullOrEmpty(tar))
{
throw new NotSupportedException($"tar -xzf");
}
// tar -xzf
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
processInvoker.OutputDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Trace.Info(args.Data);
}
});
processInvoker.ErrorDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Trace.Error(args.Data);
}
});
int exitCode = await processInvoker.ExecuteAsync(latestRunnerDirectory, tar, $"-xzf \"{archiveFile}\"", null, token);
if (exitCode != 0)
{
throw new NotSupportedException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. return code: {exitCode}.");
}
}
}
else
{
throw new NotSupportedException($"{archiveFile}");
}
Trace.Info($"Finished getting latest runner package at: {latestRunnerDirectory}.");
} }
finally finally
{ {
@@ -340,6 +211,204 @@ namespace GitHub.Runner.Listener
} }
} }
await CopyLatestRunnerToRoot(latestRunnerDirectory, token);
}
private async Task<string> DownLoadRunner(string downloadDirectory, string packageDownloadUrl, string packageHashValue, CancellationToken token)
{
var stopWatch = Stopwatch.StartNew();
int runnerSuffix = 1;
string archiveFile = null;
bool downloadSucceeded = false;
// Download the runner, using multiple attempts in order to be resilient against any networking/CDN issues
for (int attempt = 1; attempt <= Constants.RunnerDownloadRetryMaxAttempts; attempt++)
{
// Generate an available package name, and do our best effort to clean up stale local zip files
while (true)
{
if (_targetPackage.Platform.StartsWith("win"))
{
archiveFile = Path.Combine(downloadDirectory, $"runner{runnerSuffix}.zip");
}
else
{
archiveFile = Path.Combine(downloadDirectory, $"runner{runnerSuffix}.tar.gz");
}
try
{
// delete .zip file
if (!string.IsNullOrEmpty(archiveFile) && File.Exists(archiveFile))
{
Trace.Verbose("Deleting latest runner package zip '{0}'", archiveFile);
IOUtil.DeleteFile(archiveFile);
}
break;
}
catch (Exception ex)
{
// couldn't delete the file for whatever reason, so generate another name
Trace.Warning("Failed to delete runner package zip '{0}'. Exception: {1}", archiveFile, ex);
runnerSuffix++;
}
}
// Allow a 15-minute package download timeout, which is good enough to update the runner from a 1 Mbit/s ADSL connection.
if (!int.TryParse(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_DOWNLOAD_TIMEOUT") ?? string.Empty, out int timeoutSeconds))
{
timeoutSeconds = 15 * 60;
}
Trace.Info($"Attempt {attempt}: save latest runner into {archiveFile}.");
using (var downloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
using (var downloadCts = CancellationTokenSource.CreateLinkedTokenSource(downloadTimeout.Token, token))
{
try
{
Trace.Info($"Download runner: begin download");
long downloadSize = 0;
//open zip stream in async mode
using (HttpClient httpClient = new HttpClient(HostContext.CreateHttpClientHandler()))
{
if (!string.IsNullOrEmpty(_targetPackage.Token))
{
Trace.Info($"Adding authorization token ({_targetPackage.Token.Length} chars)");
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _targetPackage.Token);
}
Trace.Info($"Downloading {packageDownloadUrl}");
using (FileStream fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (Stream result = await httpClient.GetStreamAsync(packageDownloadUrl))
{
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
await result.CopyToAsync(fs, 81920, downloadCts.Token);
await fs.FlushAsync(downloadCts.Token);
downloadSize = fs.Length;
}
}
Trace.Info($"Download runner: finished download");
downloadSucceeded = true;
stopWatch.Stop();
_updateTrace.Add($"PackageDownloadTime: {stopWatch.ElapsedMilliseconds}ms");
_updateTrace.Add($"Attempts: {attempt}");
_updateTrace.Add($"PackageSize: {downloadSize / 1024 / 1024}MB");
break;
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
Trace.Info($"Runner download has been canceled.");
throw;
}
catch (Exception ex)
{
if (downloadCts.Token.IsCancellationRequested)
{
Trace.Warning($"Runner download has timed out after {timeoutSeconds} seconds");
}
Trace.Warning($"Failed to get package '{archiveFile}' from '{packageDownloadUrl}'. Exception {ex}");
}
}
}
if (downloadSucceeded)
{
return archiveFile;
}
else
{
return null;
}
}
private async Task ValidateRunnerHash(string archiveFile, string packageHashValue)
{
var stopWatch = Stopwatch.StartNew();
// Validate Hash Matches if it is provided
using (FileStream stream = File.OpenRead(archiveFile))
{
if (!string.IsNullOrEmpty(packageHashValue))
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] srcHashBytes = await sha256.ComputeHashAsync(stream);
var hash = PrimitiveExtensions.ConvertToHexString(srcHashBytes);
if (hash != packageHashValue)
{
// Hash did not match, we can't recover from this, just throw
throw new Exception($"Computed runner hash {hash} did not match expected Runner Hash {packageHashValue} for {_targetPackage.Filename}");
}
stopWatch.Stop();
Trace.Info($"Validated Runner Hash matches {_targetPackage.Filename} : {packageHashValue}");
_updateTrace.Add($"ValidateHashTime: {stopWatch.ElapsedMilliseconds}ms");
}
}
}
}
private async Task ExtractRunnerPackage(string archiveFile, string extractDirectory, CancellationToken token)
{
var stopWatch = Stopwatch.StartNew();
if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
ZipFile.ExtractToDirectory(archiveFile, extractDirectory);
}
else if (archiveFile.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))
{
string tar = WhichUtil.Which("tar", trace: Trace);
if (string.IsNullOrEmpty(tar))
{
throw new NotSupportedException($"tar -xzf");
}
// tar -xzf
using (var processInvoker = HostContext.CreateService<IProcessInvoker>())
{
processInvoker.OutputDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Trace.Info(args.Data);
}
});
processInvoker.ErrorDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
{
if (!string.IsNullOrEmpty(args.Data))
{
Trace.Error(args.Data);
}
});
int exitCode = await processInvoker.ExecuteAsync(extractDirectory, tar, $"-xzf \"{archiveFile}\"", null, token);
if (exitCode != 0)
{
throw new NotSupportedException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. return code: {exitCode}.");
}
}
}
else
{
throw new NotSupportedException($"{archiveFile}");
}
stopWatch.Stop();
Trace.Info($"Finished getting latest runner package at: {extractDirectory}.");
_updateTrace.Add($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms");
}
private Task CopyLatestRunnerToRoot(string latestRunnerDirectory, CancellationToken token)
{
var stopWatch = Stopwatch.StartNew();
// copy latest runner into runner root folder // copy latest runner into runner root folder
// copy bin from _work/_update -> bin.version under root // copy bin from _work/_update -> bin.version under root
string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}"); string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}");
@@ -357,9 +426,18 @@ namespace GitHub.Runner.Listener
Trace.Info($"Copy any remaining .sh/.cmd files into runner root."); Trace.Info($"Copy any remaining .sh/.cmd files into runner root.");
foreach (FileInfo file in new DirectoryInfo(latestRunnerDirectory).GetFiles() ?? new FileInfo[0]) foreach (FileInfo file in new DirectoryInfo(latestRunnerDirectory).GetFiles() ?? new FileInfo[0])
{ {
// Copy and replace the file. string destination = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name);
file.CopyTo(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name), true);
// Removing the file instead of just trying to overwrite it works around permissions issues on linux.
// https://github.com/actions/runner/issues/981
Trace.Info($"Copy {file.FullName} to {destination}");
IOUtil.DeleteFile(destination);
file.CopyTo(destination, true);
} }
stopWatch.Stop();
_updateTrace.Add($"CopyRunnerToRootTime: {stopWatch.ElapsedMilliseconds}ms");
return Task.CompletedTask;
} }
private void DeletePreviousVersionRunnerBackup(CancellationToken token) private void DeletePreviousVersionRunnerBackup(CancellationToken token)
@@ -483,9 +561,18 @@ namespace GitHub.Runner.Listener
{ {
_terminal.WriteLine(currentState); _terminal.WriteLine(currentState);
if (_updateTrace.Count > 0)
{
foreach (var trace in _updateTrace)
{
Trace.Info(trace);
}
}
try try
{ {
await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState); await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState, string.Join(Environment.NewLine, _updateTrace));
_updateTrace.Clear();
} }
catch (VssResourceNotFoundException) catch (VssResourceNotFoundException)
{ {

View File

@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit> <PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -444,7 +444,7 @@ namespace GitHub.Runner.Plugins.Artifact
{ {
// We should never // We should never
context.Error($"Error '{ex.Message}' when downloading file '{fileToDownload}'. (Downloader {downloaderId})"); context.Error($"Error '{ex.Message}' when downloading file '{fileToDownload}'. (Downloader {downloaderId})");
throw ex; throw;
} }
} }
@@ -528,7 +528,7 @@ namespace GitHub.Runner.Plugins.Artifact
catch (Exception ex) catch (Exception ex)
{ {
context.Output($"File error '{ex.Message}' when uploading file '{fileToUpload}'."); context.Output($"File error '{ex.Message}' when uploading file '{fileToUpload}'.");
throw ex; throw;
} }
} }

View File

@@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,19 @@
namespace GitHub.Runner.Sdk
{
/***
* WARNING: This file is automatically regenerated on layout so the runner can provide version/commit info (do not manually edit it).
*/
public static class BuildConstants
{
public static class Source
{
public static readonly string CommitHash = "N/A";
}
public static class RunnerPackage
{
public static readonly string PackageName = "N/A";
public static readonly string Version = "0";
}
}
}

View File

@@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
<Version>$(Version)</Version> <Version>$(Version)</Version>
<TieredCompilationQuickJit>true</TieredCompilationQuickJit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -47,7 +47,7 @@ namespace GitHub.Runner.Sdk
return StringUtil.ConvertFromJson<T>(json); return StringUtil.ConvertFromJson<T>(json);
} }
public static string GetPathHash(string path) public static string GetSha256Hash(string path)
{ {
string hashString = path.ToLowerInvariant(); string hashString = path.ToLowerInvariant();
using (SHA256 sha256hash = SHA256.Create()) using (SHA256 sha256hash = SHA256.Create())

View File

@@ -115,11 +115,15 @@ namespace GitHub.Runner.Sdk
} }
} }
trace?.Info("Not found."); #if OS_WINDOWS
trace?.Info($"{command}: command not found. Make sure '{command}' is installed and its location included in the 'Path' environment variable.");
#else
trace?.Info($"{command}: command not found. Make sure '{command}' is installed and its location included in the 'PATH' environment variable.");
#endif
if (require) if (require)
{ {
throw new FileNotFoundException( throw new FileNotFoundException(
message: $"File not found: '{command}'", message: $"{command}: command not found",
fileName: command); fileName: command);
} }

View File

@@ -1,7 +1,5 @@
using GitHub.DistributedTask.Pipelines; using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi; using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Worker.Container; using GitHub.Runner.Worker.Container;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -75,11 +73,19 @@ namespace GitHub.Runner.Worker
return false; return false;
} }
// process action command in serialize order. if (!ActionCommandManager.EnhancedAnnotationsEnabled(context) && actionCommand.Command == "notice")
{
context.Debug($"Enhanced Annotations not enabled on the server: 'notice' command will not be processed.");
return false;
}
// Serialize order
lock (_commandSerializeLock) lock (_commandSerializeLock)
{ {
// Currently stopped
if (_stopProcessCommand) if (_stopProcessCommand)
{ {
// Resume token
if (!string.IsNullOrEmpty(_stopToken) && if (!string.IsNullOrEmpty(_stopToken) &&
string.Equals(actionCommand.Command, _stopToken, StringComparison.OrdinalIgnoreCase)) string.Equals(actionCommand.Command, _stopToken, StringComparison.OrdinalIgnoreCase))
{ {
@@ -96,17 +102,27 @@ namespace GitHub.Runner.Worker
return false; return false;
} }
} }
// Currently processing
else else
{ {
// Stop command
if (string.Equals(actionCommand.Command, _stopCommand, StringComparison.OrdinalIgnoreCase)) if (string.Equals(actionCommand.Command, _stopCommand, StringComparison.OrdinalIgnoreCase))
{ {
context.Output(input); ValidateStopToken(context, actionCommand.Data);
context.Debug("Paused processing commands until '##[{actionCommand.Data}]' is received");
_stopToken = actionCommand.Data; _stopToken = actionCommand.Data;
_stopProcessCommand = true; _stopProcessCommand = true;
_registeredCommands.Add(_stopToken); _registeredCommands.Add(_stopToken);
if (_stopToken.Length > 6)
{
HostContext.SecretMasker.AddValue(_stopToken);
}
context.Output(input);
context.Debug("Paused processing commands until the token you called ::stopCommands:: with is received");
return true; return true;
} }
// Found command
else if (_commandExtensions.TryGetValue(actionCommand.Command, out IActionCommandExtension extension)) else if (_commandExtensions.TryGetValue(actionCommand.Command, out IActionCommandExtension extension))
{ {
if (context.EchoOnActionCommand && !extension.OmitEcho) if (context.EchoOnActionCommand && !extension.OmitEcho)
@@ -126,6 +142,7 @@ namespace GitHub.Runner.Worker
context.CommandResult = TaskResult.Failed; context.CommandResult = TaskResult.Failed;
} }
} }
// Command not found
else else
{ {
context.Warning($"Can't find command extension for ##[{actionCommand.Command}.command]."); context.Warning($"Can't find command extension for ##[{actionCommand.Command}.command].");
@@ -135,6 +152,45 @@ namespace GitHub.Runner.Worker
return true; return true;
} }
private void ValidateStopToken(IExecutionContext context, string stopToken)
{
#if OS_WINDOWS
var envContext = context.ExpressionValues["env"] as DictionaryContextData;
#else
var envContext = context.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
#endif
var allowUnsecureStopCommandTokens = false;
allowUnsecureStopCommandTokens = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedStopCommandTokens));
if (!allowUnsecureStopCommandTokens && envContext.ContainsKey(Constants.Variables.Actions.AllowUnsupportedStopCommandTokens))
{
allowUnsecureStopCommandTokens = StringUtil.ConvertToBoolean(envContext[Constants.Variables.Actions.AllowUnsupportedStopCommandTokens].ToString());
}
bool isTokenInvalid = _registeredCommands.Contains(stopToken)
|| string.IsNullOrEmpty(stopToken)
|| string.Equals(stopToken, "pause-logging", StringComparison.OrdinalIgnoreCase);
if (isTokenInvalid)
{
var telemetry = new JobTelemetry
{
Message = $"Invoked ::stopCommand:: with token: [{stopToken}]",
Type = JobTelemetryType.ActionCommand
};
context.JobTelemetry.Add(telemetry);
}
if (isTokenInvalid && !allowUnsecureStopCommandTokens)
{
throw new Exception(Constants.Runner.UnsupportedStopCommandTokenDisabled);
}
}
internal static bool EnhancedAnnotationsEnabled(IExecutionContext context)
{
return context.Global.Variables.GetBoolean("DistributedTask.EnhancedAnnotations") ?? false;
}
} }
public interface IActionCommandExtension : IExtension public interface IActionCommandExtension : IExtension
@@ -236,7 +292,7 @@ namespace GitHub.Runner.Worker
public const String Name = "name"; public const String Name = "name";
} }
private string[] _setEnvBlockList = private string[] _setEnvBlockList =
{ {
"NODE_OPTIONS" "NODE_OPTIONS"
}; };
@@ -279,8 +335,21 @@ namespace GitHub.Runner.Worker
{ {
throw new Exception("Required field 'name' is missing in ##[save-state] command."); throw new Exception("Required field 'name' is missing in ##[save-state] command.");
} }
// Embedded steps (composite) keep track of the state at the root level
context.IntraActionState[stateName] = command.Data; if (context.IsEmbedded)
{
var id = context.EmbeddedId;
if (!context.Root.EmbeddedIntraActionState.ContainsKey(id))
{
context.Root.EmbeddedIntraActionState[id] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
context.Root.EmbeddedIntraActionState[id][stateName] = command.Data;
}
// Otherwise modify the ExecutionContext
else
{
context.IntraActionState[stateName] = command.Data;
}
context.Debug($"Save intra-action state {stateName} = {command.Data}"); context.Debug($"Save intra-action state {stateName} = {command.Data}");
} }
@@ -312,6 +381,13 @@ namespace GitHub.Runner.Worker
HostContext.SecretMasker.AddValue(command.Data); HostContext.SecretMasker.AddValue(command.Data);
Trace.Info($"Add new secret mask with length of {command.Data.Length}"); Trace.Info($"Add new secret mask with length of {command.Data.Length}");
// Also add each individual line. Typically individual lines are processed from STDOUT of child processes.
var split = command.Data.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (var item in split)
{
HostContext.SecretMasker.AddValue(item);
}
} }
} }
} }
@@ -324,7 +400,7 @@ namespace GitHub.Runner.Worker
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{ {
var allowUnsecureCommands = false; var allowUnsecureCommands = false;
bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands); bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands);
@@ -492,6 +568,13 @@ namespace GitHub.Runner.Worker
public override string Command => "error"; public override string Command => "error";
} }
public sealed class NoticeCommandExtension : IssueCommandExtension
{
public override IssueType Type => IssueType.Notice;
public override string Command => "notice";
}
public abstract class IssueCommandExtension : RunnerService, IActionCommandExtension public abstract class IssueCommandExtension : RunnerService, IActionCommandExtension
{ {
public abstract IssueType Type { get; } public abstract IssueType Type { get; }
@@ -506,6 +589,11 @@ namespace GitHub.Runner.Worker
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line); command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column); command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);
if (!ActionCommandManager.EnhancedAnnotationsEnabled(context))
{
context.Debug("Enhanced Annotations not enabled on the server. The 'title', 'end_line', and 'end_column' fields are unsupported.");
}
Issue issue = new Issue() Issue issue = new Issue()
{ {
Category = "General", Category = "General",
@@ -557,13 +645,73 @@ namespace GitHub.Runner.Worker
context.AddIssue(issue); context.AddIssue(issue);
} }
public static void ValidateLinesAndColumns(ActionCommand command, IExecutionContext context)
{
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
command.Properties.TryGetValue(IssueCommandProperties.EndLine, out string endLine);
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);
command.Properties.TryGetValue(IssueCommandProperties.EndColumn, out string endColumn);
var hasStartLine = int.TryParse(line, out int lineNumber);
var hasEndLine = int.TryParse(endLine, out int endLineNumber);
var hasStartColumn = int.TryParse(column, out int columnNumber);
var hasEndColumn = int.TryParse(endColumn, out int endColumnNumber);
var hasColumn = hasStartColumn || hasEndColumn;
if (hasEndLine && !hasStartLine)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndLine}' can only be set if '{IssueCommandProperties.Line}' is provided");
command.Properties[IssueCommandProperties.Line] = endLine;
hasStartLine = true;
line = endLine;
}
if (hasEndColumn && !hasStartColumn)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndColumn}' can only be set if '{IssueCommandProperties.Column}' is provided");
command.Properties[IssueCommandProperties.Column] = endColumn;
hasStartColumn = true;
column = endColumn;
}
if (!hasStartLine && hasColumn)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' can only be set if '{IssueCommandProperties.Line}' value is provided.");
command.Properties.Remove(IssueCommandProperties.Column);
command.Properties.Remove(IssueCommandProperties.EndColumn);
}
if (hasEndLine && line != endLine && hasColumn)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.Column}' and '{IssueCommandProperties.EndColumn}' cannot be set if '{IssueCommandProperties.Line}' and '{IssueCommandProperties.EndLine}' are different values.");
command.Properties.Remove(IssueCommandProperties.Column);
command.Properties.Remove(IssueCommandProperties.EndColumn);
}
if (hasStartLine && hasEndLine && endLineNumber < lineNumber)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndLine}' cannot be less than '{IssueCommandProperties.Line}'.");
command.Properties.Remove(IssueCommandProperties.Line);
command.Properties.Remove(IssueCommandProperties.EndLine);
}
if (hasStartColumn && hasEndColumn && endColumnNumber < columnNumber)
{
context.Debug($"Invalid {command.Command} command value. '{IssueCommandProperties.EndColumn}' cannot be less than '{IssueCommandProperties.Column}'.");
command.Properties.Remove(IssueCommandProperties.Column);
command.Properties.Remove(IssueCommandProperties.EndColumn);
}
}
private static class IssueCommandProperties private static class IssueCommandProperties
{ {
public const String File = "file"; public const String File = "file";
public const String Line = "line"; public const String Line = "line";
public const String EndLine = "endLine";
public const String Column = "col"; public const String Column = "col";
public const String EndColumn = "endColumn";
public const String Title = "title";
} }
} }
public sealed class GroupCommandExtension : GroupingCommandExtension public sealed class GroupCommandExtension : GroupingCommandExtension

View File

@@ -37,7 +37,10 @@ namespace GitHub.Runner.Worker
public interface IActionManager : IRunnerService public interface IActionManager : IRunnerService
{ {
Dictionary<Guid, ContainerInfo> CachedActionContainers { get; } Dictionary<Guid, ContainerInfo> CachedActionContainers { get; }
Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps); Dictionary<Guid, List<Pipelines.ActionStep>> CachedEmbeddedPreSteps { get; }
Dictionary<Guid, List<Guid>> CachedEmbeddedStepIds { get; }
Dictionary<Guid, Stack<Pipelines.ActionStep>> CachedEmbeddedPostSteps { get; }
Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps, Guid rootStepId = default(Guid));
Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action); Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action);
} }
@@ -48,35 +51,98 @@ namespace GitHub.Runner.Worker
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k). //81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
private const int _defaultCopyBufferSize = 81920; private const int _defaultCopyBufferSize = 81920;
private const string _dotcomApiUrl = "https://api.github.com"; private const string _dotcomApiUrl = "https://api.github.com";
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers; public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers;
public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
private readonly Dictionary<Guid, List<Pipelines.ActionStep>> _cachedEmbeddedPreSteps = new Dictionary<Guid, List<Pipelines.ActionStep>>();
public Dictionary<Guid, List<Pipelines.ActionStep>> CachedEmbeddedPreSteps => _cachedEmbeddedPreSteps;
private readonly Dictionary<Guid, List<Guid>> _cachedEmbeddedStepIds = new Dictionary<Guid, List<Guid>>();
public Dictionary<Guid, List<Guid>> CachedEmbeddedStepIds => _cachedEmbeddedStepIds;
private readonly Dictionary<Guid, Stack<Pipelines.ActionStep>> _cachedEmbeddedPostSteps = new Dictionary<Guid, Stack<Pipelines.ActionStep>>();
public Dictionary<Guid, Stack<Pipelines.ActionStep>> CachedEmbeddedPostSteps => _cachedEmbeddedPostSteps;
public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps, Guid rootStepId = default(Guid))
{ {
// Assert inputs
ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext, nameof(executionContext));
ArgUtil.NotNull(steps, nameof(steps)); ArgUtil.NotNull(steps, nameof(steps));
var state = new PrepareActionsState
executionContext.Output("Prepare all required actions");
Dictionary<string, List<Guid>> imagesToPull = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase);
List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>();
Dictionary<Guid, IActionRunner> preStepTracker = new Dictionary<Guid, IActionRunner>();
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
// Log even if we aren't using it to ensure users know.
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"); ImagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase),
ImagesToPull = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase),
ImagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase),
PreStepTracker = new Dictionary<Guid, IActionRunner>()
};
var containerSetupSteps = new List<JobExtensionRunner>();
var depth = 0;
// We are running at the start of a job
if (rootStepId == default(Guid))
{
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
}
// We are running mid job due to a local composite action
else
{
if (!_cachedEmbeddedStepIds.ContainsKey(rootStepId))
{
_cachedEmbeddedStepIds[rootStepId] = new List<Guid>();
foreach (var compositeStep in steps)
{
var guid = Guid.NewGuid();
compositeStep.Id = guid;
_cachedEmbeddedStepIds[rootStepId].Add(guid);
}
}
depth = 1;
}
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
executionContext.Output("Prepare all required actions");
var result = await PrepareActionsRecursiveAsync(executionContext, state, actions, depth, rootStepId);
if (state.ImagesToPull.Count > 0)
{
foreach (var imageToPull in result.ImagesToPull)
{
Trace.Info($"{imageToPull.Value.Count} steps need to pull image '{imageToPull.Key}'");
containerSetupSteps.Add(new JobExtensionRunner(runAsync: this.PullActionContainerAsync,
condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Pull {imageToPull.Key}",
data: new ContainerSetupInfo(imageToPull.Value, imageToPull.Key)));
}
} }
// Clear the cache (for self-hosted runners) if (result.ImagesToBuild.Count > 0)
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken); {
foreach (var imageToBuild in result.ImagesToBuild)
{
var setupInfo = result.ImagesToBuildInfo[imageToBuild.Key];
Trace.Info($"{imageToBuild.Value.Count} steps need to build image from '{setupInfo.Dockerfile}'");
containerSetupSteps.Add(new JobExtensionRunner(runAsync: this.BuildActionContainerAsync,
condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Build {setupInfo.ActionRepository}",
data: new ContainerSetupInfo(imageToBuild.Value, setupInfo.Dockerfile, setupInfo.WorkingDirectory)));
}
}
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed #if !OS_LINUX
var newActionMetadata = executionContext.Global.Variables.GetBoolean("DistributedTask.NewActionMetadata") ?? false; if (containerSetupSteps.Count > 0)
{
executionContext.Output("Container action is only supported on Linux, skip pull and build docker images.");
containerSetupSteps.Clear();
}
#endif
return new PrepareResult(containerSetupSteps, result.PreStepTracker);
}
private async Task<PrepareActionsState> PrepareActionsRecursiveAsync(IExecutionContext executionContext, PrepareActionsState state, IEnumerable<Pipelines.ActionStep> actions, Int32 depth = 0, Guid parentStepId = default(Guid))
{
ArgUtil.NotNull(executionContext, nameof(executionContext));
if (depth > Constants.CompositeActionsMaxDepth)
{
throw new Exception($"Composite action depth exceeded max depth {Constants.CompositeActionsMaxDepth}");
}
var repositoryActions = new List<Pipelines.ActionStep>(); var repositoryActions = new List<Pipelines.ActionStep>();
foreach (var action in actions) foreach (var action in actions)
@@ -88,66 +154,15 @@ namespace GitHub.Runner.Worker
ArgUtil.NotNull(containerReference, nameof(containerReference)); ArgUtil.NotNull(containerReference, nameof(containerReference));
ArgUtil.NotNullOrEmpty(containerReference.Image, nameof(containerReference.Image)); ArgUtil.NotNullOrEmpty(containerReference.Image, nameof(containerReference.Image));
if (!imagesToPull.ContainsKey(containerReference.Image)) if (!state.ImagesToPull.ContainsKey(containerReference.Image))
{ {
imagesToPull[containerReference.Image] = new List<Guid>(); state.ImagesToPull[containerReference.Image] = new List<Guid>();
} }
Trace.Info($"Action {action.Name} ({action.Id}) needs to pull image '{containerReference.Image}'"); Trace.Info($"Action {action.Name} ({action.Id}) needs to pull image '{containerReference.Image}'");
imagesToPull[containerReference.Image].Add(action.Id); state.ImagesToPull[containerReference.Image].Add(action.Id);
} }
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed else if (action.Reference.Type == Pipelines.ActionSourceType.Repository)
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository && !newActionMetadata)
{
// only download the repository archive
await DownloadRepositoryActionAsync(executionContext, action);
// more preparation base on content in the repository (action.yml)
var setupInfo = PrepareRepositoryActionAsync(executionContext, action);
if (setupInfo != null)
{
if (!string.IsNullOrEmpty(setupInfo.Image))
{
if (!imagesToPull.ContainsKey(setupInfo.Image))
{
imagesToPull[setupInfo.Image] = new List<Guid>();
}
Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to pull image '{setupInfo.Image}'");
imagesToPull[setupInfo.Image].Add(action.Id);
}
else
{
ArgUtil.NotNullOrEmpty(setupInfo.ActionRepository, nameof(setupInfo.ActionRepository));
if (!imagesToBuild.ContainsKey(setupInfo.ActionRepository))
{
imagesToBuild[setupInfo.ActionRepository] = new List<Guid>();
}
Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to build image '{setupInfo.Dockerfile}'");
imagesToBuild[setupInfo.ActionRepository].Add(action.Id);
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo;
}
}
var repoAction = action.Reference as Pipelines.RepositoryPathReference;
if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias)
{
var definition = LoadAction(executionContext, action);
if (definition.Data.Execution.HasPre)
{
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = action;
actionRunner.Stage = ActionRunStage.Pre;
actionRunner.Condition = definition.Data.Execution.InitCondition;
Trace.Info($"Add 'pre' execution for {action.Id}");
preStepTracker[action.Id] = actionRunner;
}
}
}
else if (action.Reference.Type == Pipelines.ActionSourceType.Repository && newActionMetadata)
{ {
repositoryActions.Add(action); repositoryActions.Add(action);
} }
@@ -179,85 +194,96 @@ namespace GitHub.Runner.Worker
foreach (var action in repositoryActions) foreach (var action in repositoryActions)
{ {
var setupInfo = PrepareRepositoryActionAsync(executionContext, action); var setupInfo = PrepareRepositoryActionAsync(executionContext, action);
if (setupInfo != null) if (setupInfo != null && setupInfo.Container != null)
{ {
if (!string.IsNullOrEmpty(setupInfo.Image)) if (!string.IsNullOrEmpty(setupInfo.Container.Image))
{ {
if (!imagesToPull.ContainsKey(setupInfo.Image)) if (!state.ImagesToPull.ContainsKey(setupInfo.Container.Image))
{ {
imagesToPull[setupInfo.Image] = new List<Guid>(); state.ImagesToPull[setupInfo.Container.Image] = new List<Guid>();
} }
Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to pull image '{setupInfo.Image}'"); Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.Container.ActionRepository}' needs to pull image '{setupInfo.Container.Image}'");
imagesToPull[setupInfo.Image].Add(action.Id); state.ImagesToPull[setupInfo.Container.Image].Add(action.Id);
} }
else else
{ {
ArgUtil.NotNullOrEmpty(setupInfo.ActionRepository, nameof(setupInfo.ActionRepository)); ArgUtil.NotNullOrEmpty(setupInfo.Container.ActionRepository, nameof(setupInfo.Container.ActionRepository));
if (!imagesToBuild.ContainsKey(setupInfo.ActionRepository)) if (!state.ImagesToBuild.ContainsKey(setupInfo.Container.ActionRepository))
{ {
imagesToBuild[setupInfo.ActionRepository] = new List<Guid>(); state.ImagesToBuild[setupInfo.Container.ActionRepository] = new List<Guid>();
} }
Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.ActionRepository}' needs to build image '{setupInfo.Dockerfile}'"); Trace.Info($"Action {action.Name} ({action.Id}) from repository '{setupInfo.Container.ActionRepository}' needs to build image '{setupInfo.Container.Dockerfile}'");
imagesToBuild[setupInfo.ActionRepository].Add(action.Id); state.ImagesToBuild[setupInfo.Container.ActionRepository].Add(action.Id);
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; state.ImagesToBuildInfo[setupInfo.Container.ActionRepository] = setupInfo.Container;
} }
} }
else if (setupInfo != null && setupInfo.Steps != null && setupInfo.Steps.Count > 0)
{
state = await PrepareActionsRecursiveAsync(executionContext, state, setupInfo.Steps, depth + 1, action.Id);
}
var repoAction = action.Reference as Pipelines.RepositoryPathReference; var repoAction = action.Reference as Pipelines.RepositoryPathReference;
if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias) if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias)
{ {
var definition = LoadAction(executionContext, action); var definition = LoadAction(executionContext, action);
if (definition.Data.Execution.HasPre) if (definition.Data.Execution.HasPre)
{ {
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = action;
actionRunner.Stage = ActionRunStage.Pre;
actionRunner.Condition = definition.Data.Execution.InitCondition;
Trace.Info($"Add 'pre' execution for {action.Id}"); Trace.Info($"Add 'pre' execution for {action.Id}");
preStepTracker[action.Id] = actionRunner; // Root Step
if (depth < 1)
{
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = action;
actionRunner.Stage = ActionRunStage.Pre;
actionRunner.Condition = definition.Data.Execution.InitCondition;
state.PreStepTracker[action.Id] = actionRunner;
}
// Embedded Step
else
{
if (!_cachedEmbeddedPreSteps.ContainsKey(parentStepId))
{
_cachedEmbeddedPreSteps[parentStepId] = new List<Pipelines.ActionStep>();
}
// Clone action so we can modify the condition without affecting the original
var clonedAction = action.Clone() as Pipelines.ActionStep;
clonedAction.Condition = definition.Data.Execution.InitCondition;
_cachedEmbeddedPreSteps[parentStepId].Add(clonedAction);
}
} }
if (definition.Data.Execution.HasPost && depth > 0)
{
if (!_cachedEmbeddedPostSteps.ContainsKey(parentStepId))
{
// If we haven't done so already, add the parent to the post steps
_cachedEmbeddedPostSteps[parentStepId] = new Stack<Pipelines.ActionStep>();
}
// Clone action so we can modify the condition without affecting the original
var clonedAction = action.Clone() as Pipelines.ActionStep;
clonedAction.Condition = definition.Data.Execution.CleanupCondition;
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
}
}
else if (depth > 0)
{
// if we're in a composite action and haven't loaded the local action yet
// we assume it has a post step
if (!_cachedEmbeddedPostSteps.ContainsKey(parentStepId))
{
// If we haven't done so already, add the parent to the post steps
_cachedEmbeddedPostSteps[parentStepId] = new Stack<Pipelines.ActionStep>();
}
// Clone action so we can modify the condition without affecting the original
var clonedAction = action.Clone() as Pipelines.ActionStep;
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
} }
} }
} }
if (imagesToPull.Count > 0) return state;
{
foreach (var imageToPull in imagesToPull)
{
Trace.Info($"{imageToPull.Value.Count} steps need to pull image '{imageToPull.Key}'");
containerSetupSteps.Add(new JobExtensionRunner(runAsync: this.PullActionContainerAsync,
condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Pull {imageToPull.Key}",
data: new ContainerSetupInfo(imageToPull.Value, imageToPull.Key)));
}
}
if (imagesToBuild.Count > 0)
{
foreach (var imageToBuild in imagesToBuild)
{
var setupInfo = imagesToBuildInfo[imageToBuild.Key];
Trace.Info($"{imageToBuild.Value.Count} steps need to build image from '{setupInfo.Dockerfile}'");
containerSetupSteps.Add(new JobExtensionRunner(runAsync: this.BuildActionContainerAsync,
condition: $"{PipelineTemplateConstants.Success}()",
displayName: $"Build {setupInfo.ActionRepository}",
data: new ContainerSetupInfo(imageToBuild.Value, setupInfo.Dockerfile, setupInfo.WorkingDirectory)));
}
}
#if !OS_LINUX
if (containerSetupSteps.Count > 0)
{
executionContext.Output("Container action is only supported on Linux, skip pull and build docker images.");
containerSetupSteps.Clear();
}
#endif
return new PrepareResult(containerSetupSteps, preStepTracker);
} }
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action) public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action)
@@ -402,6 +428,39 @@ namespace GitHub.Runner.Worker
Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction?.Steps)}"); Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction?.Steps)}");
Trace.Info($"Load: {compositeAction.Outputs?.Count ?? 0} number of outputs"); Trace.Info($"Load: {compositeAction.Outputs?.Count ?? 0} number of outputs");
Trace.Info($"Details: {StringUtil.ConvertToJson(compositeAction?.Outputs)}"); Trace.Info($"Details: {StringUtil.ConvertToJson(compositeAction?.Outputs)}");
if (CachedEmbeddedPreSteps.TryGetValue(action.Id, out var preSteps))
{
compositeAction.PreSteps = preSteps;
}
if (CachedEmbeddedPostSteps.TryGetValue(action.Id, out var postSteps))
{
compositeAction.PostSteps = postSteps;
}
if (_cachedEmbeddedStepIds.ContainsKey(action.Id))
{
for (var i = 0; i < compositeAction.Steps.Count; i++)
{
// Load stored Ids for later load actions
compositeAction.Steps[i].Id = _cachedEmbeddedStepIds[action.Id][i];
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && compositeAction.Steps[i].Reference.Type != Pipelines.ActionSourceType.Script)
{
throw new Exception("`uses:` keyword is not currently supported.");
}
}
}
else
{
_cachedEmbeddedStepIds[action.Id] = new List<Guid>();
foreach (var compositeStep in compositeAction.Steps)
{
var guid = Guid.NewGuid();
compositeStep.Id = guid;
_cachedEmbeddedStepIds[action.Id].Add(guid);
}
}
} }
else else
{ {
@@ -471,12 +530,12 @@ namespace GitHub.Runner.Worker
executionContext.Output($"##[group]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 dockerManager = HostContext.GetService<IDockerCommandManager>();
int retryCount = 0; int retryCount = 0;
int pullExitCode = 0; int pullExitCode = 0;
while (retryCount < 3) while (retryCount < 3)
{ {
pullExitCode = await dockerManger.DockerPull(executionContext, setupInfo.Container.Image); pullExitCode = await dockerManager.DockerPull(executionContext, setupInfo.Container.Image);
if (pullExitCode == 0) if (pullExitCode == 0)
{ {
break; break;
@@ -515,13 +574,13 @@ namespace GitHub.Runner.Worker
executionContext.Output($"##[group]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 dockerManager = HostContext.GetService<IDockerCommandManager>();
int retryCount = 0; int retryCount = 0;
int buildExitCode = 0; int buildExitCode = 0;
var imageName = $"{dockerManger.DockerInstanceLabel}:{Guid.NewGuid().ToString("N")}"; var imageName = $"{dockerManager.DockerInstanceLabel}:{Guid.NewGuid().ToString("N")}";
while (retryCount < 3) while (retryCount < 3)
{ {
buildExitCode = await dockerManger.DockerBuild( buildExitCode = await dockerManager.DockerBuild(
executionContext, executionContext,
setupInfo.Container.WorkingDirectory, setupInfo.Container.WorkingDirectory,
setupInfo.Container.Dockerfile, setupInfo.Container.Dockerfile,
@@ -574,6 +633,7 @@ namespace GitHub.Runner.Worker
{ {
NameWithOwner = repositoryReference.Name, NameWithOwner = repositoryReference.Name,
Ref = repositoryReference.Ref, Ref = repositoryReference.Ref,
Path = repositoryReference.Path,
}; };
}) })
.ToList(); .ToList();
@@ -596,7 +656,12 @@ namespace GitHub.Runner.Worker
} }
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is canceled. catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is canceled.
{ {
if (attempt < 3) // UnresolvableActionDownloadInfoException is a 422 client error, don't retry
// Some possible cases are:
// * Repo is rate limited
// * Repo or tag doesn't exist, or isn't public
// * Policy validation failed
if (attempt < 3 && !(ex is WebApi.UnresolvableActionDownloadInfoException))
{ {
executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}"); executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}");
executionContext.Debug(ex.ToString()); executionContext.Debug(ex.ToString());
@@ -612,6 +677,7 @@ namespace GitHub.Runner.Worker
// Some possible cases are: // Some possible cases are:
// * Repo is rate limited // * Repo is rate limited
// * Repo or tag doesn't exist, or isn't public // * Repo or tag doesn't exist, or isn't public
// * Policy validation failed
if (ex is WebApi.UnresolvableActionDownloadInfoException) if (ex is WebApi.UnresolvableActionDownloadInfoException)
{ {
throw; throw;
@@ -647,90 +713,6 @@ namespace GitHub.Runner.Worker
return actionDownloadInfos.Actions; return actionDownloadInfos.Actions;
} }
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed
private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
{
Trace.Entering();
ArgUtil.NotNull(executionContext, nameof(executionContext));
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
ArgUtil.NotNull(repositoryReference, nameof(repositoryReference));
if (string.Equals(repositoryReference.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
{
Trace.Info($"Repository action is in 'self' repository.");
return;
}
if (!string.Equals(repositoryReference.RepositoryType, Pipelines.RepositoryTypes.GitHub, StringComparison.OrdinalIgnoreCase))
{
throw new NotSupportedException(repositoryReference.RepositoryType);
}
ArgUtil.NotNullOrEmpty(repositoryReference.Name, nameof(repositoryReference.Name));
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
string watermarkFile = GetWatermarkFilePath(destDirectory);
if (File.Exists(watermarkFile))
{
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
return;
}
else
{
// make sure we get a clean folder ready to use.
IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken);
Directory.CreateDirectory(destDirectory);
executionContext.Output($"Download action repository '{repositoryReference.Name}@{repositoryReference.Ref}'");
}
var configurationStore = HostContext.GetService<IConfigurationStore>();
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
if (isHostedServer)
{
string apiUrl = GetApiUrl(executionContext);
string archiveLink = BuildLinkToActionArchive(apiUrl, repositoryReference.Name, repositoryReference.Ref);
var downloadDetails = new ActionDownloadDetails(archiveLink, ConfigureAuthorizationFromContext);
await DownloadRepositoryActionAsync(executionContext, downloadDetails, null, destDirectory);
return;
}
else
{
string apiUrl = GetApiUrl(executionContext);
// URLs to try:
var downloadAttempts = new List<ActionDownloadDetails> {
// A built-in action or an action the user has created, on their GHES instance
// Example: https://my-ghes/api/v3/repos/my-org/my-action/tarball/v1
new ActionDownloadDetails(
BuildLinkToActionArchive(apiUrl, repositoryReference.Name, repositoryReference.Ref),
ConfigureAuthorizationFromContext),
// The same action, on GitHub.com
// Example: https://api.github.com/repos/my-org/my-action/tarball/v1
new ActionDownloadDetails(
BuildLinkToActionArchive(_dotcomApiUrl, repositoryReference.Name, repositoryReference.Ref),
configureAuthorization: (e,h) => { /* no authorization for dotcom */ })
};
foreach (var downloadAttempt in downloadAttempts)
{
try
{
await DownloadRepositoryActionAsync(executionContext, downloadAttempt, null, destDirectory);
return;
}
catch (ActionNotFoundException)
{
Trace.Info($"Failed to find the action '{repositoryReference.Name}' at ref '{repositoryReference.Ref}' at {downloadAttempt.ArchiveLink}");
continue;
}
}
throw new ActionNotFoundException($"Failed to find the action '{repositoryReference.Name}' at ref '{repositoryReference.Ref}'. Paths attempted: {string.Join(", ", downloadAttempts.Select(d => d.ArchiveLink))}");
}
}
private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, WebApi.ActionDownloadInfo downloadInfo) private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, WebApi.ActionDownloadInfo downloadInfo)
{ {
Trace.Entering(); Trace.Entering();
@@ -751,10 +733,10 @@ namespace GitHub.Runner.Worker
// make sure we get a clean folder ready to use. // make sure we get a clean folder ready to use.
IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken); IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken);
Directory.CreateDirectory(destDirectory); Directory.CreateDirectory(destDirectory);
executionContext.Output($"Download action repository '{downloadInfo.NameWithOwner}@{downloadInfo.Ref}'"); executionContext.Output($"Download action repository '{downloadInfo.NameWithOwner}@{downloadInfo.Ref}' (SHA:{downloadInfo.ResolvedSha})");
} }
await DownloadRepositoryActionAsync(executionContext, null, downloadInfo, destDirectory); await DownloadRepositoryActionAsync(executionContext, downloadInfo, destDirectory);
} }
private string GetApiUrl(IExecutionContext executionContext) private string GetApiUrl(IExecutionContext executionContext)
@@ -777,8 +759,7 @@ namespace GitHub.Runner.Worker
#endif #endif
} }
// todo: Remove the parameter "actionDownloadDetails" when feature flag DistributedTask.NewActionMetadata is removed private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, WebApi.ActionDownloadInfo downloadInfo, string destDirectory)
private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, ActionDownloadDetails actionDownloadDetails, WebApi.ActionDownloadInfo downloadInfo, string destDirectory)
{ {
//download and extract action in a temp folder and rename it on success //download and extract action in a temp folder and rename it on success
string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid()); string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid());
@@ -786,10 +767,10 @@ namespace GitHub.Runner.Worker
#if OS_WINDOWS #if OS_WINDOWS
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.zip"); string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.zip");
string link = downloadInfo?.ZipballUrl ?? actionDownloadDetails.ArchiveLink; string link = downloadInfo?.ZipballUrl;
#else #else
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.tar.gz"); string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.tar.gz");
string link = downloadInfo?.TarballUrl ?? actionDownloadDetails.ArchiveLink; string link = downloadInfo?.TarballUrl;
#endif #endif
Trace.Info($"Save archive '{link}' into {archiveFile}."); Trace.Info($"Save archive '{link}' into {archiveFile}.");
@@ -811,16 +792,7 @@ namespace GitHub.Runner.Worker
using (var httpClientHandler = HostContext.CreateHttpClientHandler()) using (var httpClientHandler = HostContext.CreateHttpClientHandler())
using (var httpClient = new HttpClient(httpClientHandler)) using (var httpClient = new HttpClient(httpClientHandler))
{ {
// Legacy httpClient.DefaultRequestHeaders.Authorization = CreateAuthHeader(downloadInfo.Authentication?.Token);
if (downloadInfo == null)
{
actionDownloadDetails.ConfigureAuthorization(executionContext, httpClient);
}
// FF DistributedTask.NewActionMetadata
else
{
httpClient.DefaultRequestHeaders.Authorization = CreateAuthHeader(downloadInfo.Authentication?.Token);
}
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents); httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
using (var response = await httpClient.GetAsync(link)) using (var response = await httpClient.GetAsync(link))
@@ -960,7 +932,6 @@ namespace GitHub.Runner.Worker
} }
} }
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed
private void ConfigureAuthorizationFromContext(IExecutionContext executionContext, HttpClient httpClient) private void ConfigureAuthorizationFromContext(IExecutionContext executionContext, HttpClient httpClient)
{ {
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN"); var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
@@ -986,7 +957,7 @@ namespace GitHub.Runner.Worker
private string GetWatermarkFilePath(string directory) => directory + ".completed"; private string GetWatermarkFilePath(string directory) => directory + ".completed";
private ActionContainer PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction) private ActionSetupInfo PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
{ {
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference; var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
if (string.Equals(repositoryReference.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)) if (string.Equals(repositoryReference.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
@@ -994,8 +965,8 @@ namespace GitHub.Runner.Worker
Trace.Info($"Repository action is in 'self' repository."); Trace.Info($"Repository action is in 'self' repository.");
return null; return null;
} }
var setupInfo = new ActionSetupInfo();
var setupInfo = new ActionContainer(); var actionContainer = new ActionContainer();
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref); string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
string actionEntryDirectory = destDirectory; string actionEntryDirectory = destDirectory;
string dockerFileRelativePath = repositoryReference.Name; string dockerFileRelativePath = repositoryReference.Name;
@@ -1004,11 +975,11 @@ namespace GitHub.Runner.Worker
{ {
actionEntryDirectory = Path.Combine(destDirectory, repositoryReference.Path); actionEntryDirectory = Path.Combine(destDirectory, repositoryReference.Path);
dockerFileRelativePath = $"{dockerFileRelativePath}/{repositoryReference.Path}"; dockerFileRelativePath = $"{dockerFileRelativePath}/{repositoryReference.Path}";
setupInfo.ActionRepository = $"{repositoryReference.Name}/{repositoryReference.Path}@{repositoryReference.Ref}"; actionContainer.ActionRepository = $"{repositoryReference.Name}/{repositoryReference.Path}@{repositoryReference.Ref}";
} }
else else
{ {
setupInfo.ActionRepository = $"{repositoryReference.Name}@{repositoryReference.Ref}"; actionContainer.ActionRepository = $"{repositoryReference.Name}@{repositoryReference.Ref}";
} }
// find the docker file or action.yml file // find the docker file or action.yml file
@@ -1038,8 +1009,9 @@ namespace GitHub.Runner.Worker
var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image); var dockerFileFullPath = Path.Combine(actionEntryDirectory, containerAction.Image);
executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'."); executionContext.Debug($"Dockerfile for action: '{dockerFileFullPath}'.");
setupInfo.Dockerfile = dockerFileFullPath; actionContainer.Dockerfile = dockerFileFullPath;
setupInfo.WorkingDirectory = destDirectory; actionContainer.WorkingDirectory = destDirectory;
setupInfo.Container = actionContainer;
return setupInfo; return setupInfo;
} }
else if (containerAction.Image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase)) else if (containerAction.Image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase))
@@ -1048,7 +1020,8 @@ namespace GitHub.Runner.Worker
executionContext.Debug($"Container image for action: '{actionImage}'."); executionContext.Debug($"Container image for action: '{actionImage}'.");
setupInfo.Image = actionImage; actionContainer.Image = actionImage;
setupInfo.Container = actionContainer;
return setupInfo; return setupInfo;
} }
else else
@@ -1068,8 +1041,30 @@ namespace GitHub.Runner.Worker
} }
else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite) else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite)
{ {
Trace.Info($"Action composite: {(actionDefinitionData.Execution as CompositeActionExecutionData).Steps}, no more preparation."); Trace.Info($"Loading Composite steps");
return null; var compositeAction = actionDefinitionData.Execution as CompositeActionExecutionData;
setupInfo.Steps = compositeAction.Steps;
// cache steps ids if not done so already
if (!_cachedEmbeddedStepIds.ContainsKey(repositoryAction.Id))
{
_cachedEmbeddedStepIds[repositoryAction.Id] = new List<Guid>();
foreach (var compositeStep in compositeAction.Steps)
{
var guid = Guid.NewGuid();
compositeStep.Id = guid;
_cachedEmbeddedStepIds[repositoryAction.Id].Add(guid);
}
}
foreach (var step in compositeAction.Steps)
{
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && step.Reference.Type != Pipelines.ActionSourceType.Script)
{
throw new Exception("`uses:` keyword is not currently supported.");
}
}
return setupInfo;
} }
else else
{ {
@@ -1079,15 +1074,17 @@ namespace GitHub.Runner.Worker
else if (File.Exists(dockerFile)) else if (File.Exists(dockerFile))
{ {
executionContext.Debug($"Dockerfile for action: '{dockerFile}'."); executionContext.Debug($"Dockerfile for action: '{dockerFile}'.");
setupInfo.Dockerfile = dockerFile; actionContainer.Dockerfile = dockerFile;
setupInfo.WorkingDirectory = destDirectory; actionContainer.WorkingDirectory = destDirectory;
setupInfo.Container = actionContainer;
return setupInfo; return setupInfo;
} }
else if (File.Exists(dockerFileLowerCase)) else if (File.Exists(dockerFileLowerCase))
{ {
executionContext.Debug($"Dockerfile for action: '{dockerFileLowerCase}'."); executionContext.Debug($"Dockerfile for action: '{dockerFileLowerCase}'.");
setupInfo.Dockerfile = dockerFileLowerCase; actionContainer.Dockerfile = dockerFileLowerCase;
setupInfo.WorkingDirectory = destDirectory; actionContainer.WorkingDirectory = destDirectory;
setupInfo.Container = actionContainer;
return setupInfo; return setupInfo;
} }
else else
@@ -1140,20 +1137,6 @@ namespace GitHub.Runner.Worker
HostContext.SecretMasker.AddValue(base64EncodingToken); HostContext.SecretMasker.AddValue(base64EncodingToken);
return new AuthenticationHeaderValue("Basic", base64EncodingToken); return new AuthenticationHeaderValue("Basic", base64EncodingToken);
} }
// todo: Remove when feature flag DistributedTask.NewActionMetadata is removed
private class ActionDownloadDetails
{
public string ArchiveLink { get; }
public Action<IExecutionContext, HttpClient> ConfigureAuthorization { get; }
public ActionDownloadDetails(string archiveLink, Action<IExecutionContext, HttpClient> configureAuthorization)
{
ArchiveLink = archiveLink;
ConfigureAuthorization = configureAuthorization;
}
}
} }
public sealed class Definition public sealed class Definition
@@ -1216,6 +1199,8 @@ namespace GitHub.Runner.Worker
public string Pre { get; set; } public string Pre { get; set; }
public string Post { get; set; } public string Post { get; set; }
public string NodeVersion { get; set; }
} }
public sealed class PluginActionExecutionData : ActionExecutionData public sealed class PluginActionExecutionData : ActionExecutionData
@@ -1241,9 +1226,11 @@ namespace GitHub.Runner.Worker
public sealed class CompositeActionExecutionData : ActionExecutionData public sealed class CompositeActionExecutionData : ActionExecutionData
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Composite; public override ActionExecutionType ExecutionType => ActionExecutionType.Composite;
public override bool HasPre => false; public override bool HasPre => PreSteps.Count > 0;
public override bool HasPost => false; public override bool HasPost => PostSteps.Count > 0;
public List<Pipelines.ActionStep> PreSteps { get; set; }
public List<Pipelines.ActionStep> Steps { get; set; } public List<Pipelines.ActionStep> Steps { get; set; }
public Stack<Pipelines.ActionStep> PostSteps { get; set; }
public MappingToken Outputs { get; set; } public MappingToken Outputs { get; set; }
} }
@@ -1303,4 +1290,18 @@ namespace GitHub.Runner.Worker
public string WorkingDirectory { get; set; } public string WorkingDirectory { get; set; }
public string ActionRepository { get; set; } public string ActionRepository { get; set; }
} }
public class ActionSetupInfo
{
public ActionContainer Container { get; set; }
public List<Pipelines.ActionStep> Steps { get; set; }
}
public class PrepareActionsState
{
public Dictionary<string, List<Guid>> ImagesToPull;
public Dictionary<string, List<Guid>> ImagesToBuild;
public Dictionary<string, ActionContainer> ImagesToBuildInfo;
public Dictionary<Guid, IActionRunner> PreStepTracker;
}
} }

View File

@@ -311,7 +311,7 @@ namespace GitHub.Runner.Worker
var result = new TemplateContext var result = new TemplateContext
{ {
CancellationToken = CancellationToken.None, CancellationToken = CancellationToken.None,
Errors = new TemplateValidationErrors(10, 500), Errors = new TemplateValidationErrors(10, int.MaxValue), // Don't truncate error messages otherwise we might not scrub secrets correctly
Memory = new TemplateMemory( Memory = new TemplateMemory(
maxDepth: 100, maxDepth: 100,
maxEvents: 1000000, maxEvents: 1000000,
@@ -451,7 +451,8 @@ namespace GitHub.Runner.Worker
}; };
} }
} }
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)||
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
{ {
if (string.IsNullOrEmpty(mainToken?.Value)) if (string.IsNullOrEmpty(mainToken?.Value))
{ {
@@ -461,6 +462,7 @@ namespace GitHub.Runner.Worker
{ {
return new NodeJSActionExecutionData() return new NodeJSActionExecutionData()
{ {
NodeVersion = usingToken.Value,
Script = mainToken.Value, Script = mainToken.Value,
Pre = preToken?.Value, Pre = preToken?.Value,
InitCondition = preIfToken?.Value ?? "always()", InitCondition = preIfToken?.Value ?? "always()",
@@ -480,13 +482,17 @@ namespace GitHub.Runner.Worker
return new CompositeActionExecutionData() return new CompositeActionExecutionData()
{ {
Steps = steps.Cast<Pipelines.ActionStep>().ToList(), Steps = steps.Cast<Pipelines.ActionStep>().ToList(),
PreSteps = new List<Pipelines.ActionStep>(),
PostSteps = new Stack<Pipelines.ActionStep>(),
InitCondition = "always()",
CleanupCondition = "always()",
Outputs = outputs 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', 'node12' or 'node16' instead.");
} }
} }
else if (pluginToken != null) else if (pluginToken != null)

View File

@@ -82,6 +82,28 @@ namespace GitHub.Runner.Worker
ActionExecutionData handlerData = definition.Data?.Execution; ActionExecutionData handlerData = definition.Data?.Execution;
ArgUtil.NotNull(handlerData, nameof(handlerData)); ArgUtil.NotNull(handlerData, nameof(handlerData));
List<JobExtensionRunner> localActionContainerSetupSteps = null;
// Handle Composite Local Actions
// Need to download and expand the tree of referenced actions
if (handlerData.ExecutionType == ActionExecutionType.Composite &&
handlerData is CompositeActionExecutionData compositeHandlerData &&
Stage == ActionRunStage.Main &&
Action.Reference is Pipelines.RepositoryPathReference localAction &&
string.Equals(localAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
{
var actionManager = HostContext.GetService<IActionManager>();
var prepareResult = await actionManager.PrepareActionsAsync(ExecutionContext, compositeHandlerData.Steps, ExecutionContext.Id);
// Reload definition since post may exist now (from embedded steps that were JIT downloaded)
definition = taskManager.LoadAction(ExecutionContext, Action);
ArgUtil.NotNull(definition, nameof(definition));
handlerData = definition.Data?.Execution;
ArgUtil.NotNull(handlerData, nameof(handlerData));
// Save container setup steps so we can reference them later
localActionContainerSetupSteps = prepareResult.ContainerSetupSteps;
}
if (handlerData.HasPre && if (handlerData.HasPre &&
Action.Reference is Pipelines.RepositoryPathReference repoAction && Action.Reference is Pipelines.RepositoryPathReference repoAction &&
string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)) string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase))
@@ -249,7 +271,8 @@ namespace GitHub.Runner.Worker
inputs, inputs,
environment, environment,
ExecutionContext.Global.Variables, ExecutionContext.Global.Variables,
actionDirectory: definition.Directory); actionDirectory: definition.Directory,
localActionContainerSetupSteps: localActionContainerSetupSteps);
// Print out action details // Print out action details
handler.PrintActionDetails(Stage); handler.PrintActionDetails(Stage);

View File

@@ -0,0 +1,47 @@
using System;
using System.Text;
using GitHub.DistributedTask.Pipelines;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
namespace GitHub.Runner.Worker
{
public sealed class ConditionTraceWriter : ObjectTemplating::ITraceWriter
{
private readonly IExecutionContext _executionContext;
private readonly Tracing _trace;
private readonly StringBuilder _traceBuilder = new StringBuilder();
public string Trace => _traceBuilder.ToString();
public ConditionTraceWriter(Tracing trace, IExecutionContext executionContext)
{
ArgUtil.NotNull(trace, nameof(trace));
_trace = trace;
_executionContext = executionContext;
}
public void Error(string format, params Object[] args)
{
var message = StringUtil.Format(format, args);
_trace.Error(message);
_executionContext?.Debug(message);
}
public void Info(string format, params Object[] args)
{
var message = StringUtil.Format(format, args);
_trace.Info(message);
_executionContext?.Debug(message);
_traceBuilder.AppendLine(message);
}
public void Verbose(string format, params Object[] args)
{
var message = StringUtil.Format(format, args);
_trace.Verbose(message);
_executionContext?.Debug(message);
}
}
}

View File

@@ -46,7 +46,7 @@ namespace GitHub.Runner.Worker.Container
{ {
base.Initialize(hostContext); base.Initialize(hostContext);
DockerPath = WhichUtil.Which("docker", true, Trace); DockerPath = WhichUtil.Which("docker", true, Trace);
DockerInstanceLabel = IOUtil.GetPathHash(hostContext.GetDirectory(WellKnownDirectory.Root)).Substring(0, 6); DockerInstanceLabel = IOUtil.GetSha256Hash(hostContext.GetDirectory(WellKnownDirectory.Root)).Substring(0, 6);
} }
public async Task<DockerVersion> DockerVersion(IExecutionContext context) public async Task<DockerVersion> DockerVersion(IExecutionContext context)
@@ -197,8 +197,7 @@ namespace GitHub.Runner.Worker.Container
dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}"); dockerOptions.Add($"--workdir {container.ContainerWorkDirectory}");
dockerOptions.Add($"--rm"); dockerOptions.Add($"--rm");
dockerOptions.Add($"-t");
foreach (var env in container.ContainerEnvironmentVariables) foreach (var env in container.ContainerEnvironmentVariables)
{ {
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing // e.g. -e MY_SECRET maps the value into the exec'ed process without exposing

Some files were not shown because too many files have changed in this diff Show More