build: use Task (#1535)

This commit is contained in:
Vinicius Fortuna 2024-04-17 19:48:23 -04:00 committed by GitHub
parent be0d3b3838
commit 6556d7e595
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 653 additions and 920 deletions

View file

@ -31,7 +31,7 @@ jobs:
run: npm ci
- name: Lint
run: npm run lint
run: ./task lint
shadowbox:
name: Shadowbox
@ -51,13 +51,13 @@ jobs:
run: npm ci
- name: Shadowbox Debug Build
run: npm run action shadowbox/server/build
run: ./task shadowbox:build
- name: Shadowbox Unit Test
run: npm run action shadowbox/test
run: ./task shadowbox:test
- name: Shadowbox Integration Test
run: npm run action shadowbox/integration_test/run
run: ./task shadowbox:integration_test
manual-install-script:
name: Manual Install Script
@ -90,10 +90,10 @@ jobs:
run: npm ci
- name: Metrics Server Debug Build
run: npm run action metrics_server/build
run: ./task metrics_server:build
- name: Metrics Server Test
run: npm run action metrics_server/test
run: ./task metrics_server:test
sentry-webhook:
name: Sentry Webhook
@ -113,7 +113,7 @@ jobs:
run: npm ci
- name: Sentry Webhook Debug Build
run: npm run action sentry_webhook/build
run: ./task sentry_webhook:build
- name: Sentry Webhook Test
run: npm run action sentry_webhook/test
run: ./task sentry_webhook:test

9
.gitignore vendored
View file

@ -1,9 +1,10 @@
.DS_Store
.idea/
.vscode/
/build/
node_modules/
/src/server_manager/install_scripts/do_install_script.ts
/src/server_manager/install_scripts/gcp_install_script.ts
.vscode/
.idea/
third_party/shellcheck/download/
/task
macos-signing-certificate.p12
node_modules/
third_party/shellcheck/download/

View file

@ -24,26 +24,16 @@ use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Build Actions
## Build Tasks
We have a very simple build system based on package.json scripts that are called using `npm run`
and a thin wrapper for what we call build "actions".
We've defined a package.json script called `action` whose parameter is a relative path:
We use [Task](https://taskfile.dev/) to run tasks. [Install Task](https://taskfile.dev/installation/) and make sure it's in your `PATH`. Then you can run tasks with:
```sh
npm run action $ACTION
task [task_name]
```
This command will define a `run_action()` function and call `${ACTION}.action.sh`, which must exist.
The called action script can use `run_action` to call its dependencies. The $ACTION parameter is
always resolved from the project root, regardless of the caller location.
To list all available tasks:
The idea of `run_action` is to keep the build logic next to where the relevant code is.
It also defines two environmental variables:
- `ROOT_DIR`: the root directory of the project, as an absolute path.
- `BUILD_DIR`: where the build output should go, as an absolute path.
> [!TIP]
> To find all the actions in this project, run `npm run action:list`
```sh
task -a
```

View file

@ -31,6 +31,7 @@ See [Shadowsocks resistance against detection and blocking](docs/shadowsocks.md)
- [Node](https://nodejs.org/en/download/) LTS (`lts/hydrogen`, version `18.16.0`)
- [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (version `9.5.1`)
- [Go](https://go.dev/dl/) 1.21+
1. **Install dependencies**
@ -41,7 +42,7 @@ See [Shadowsocks resistance against detection and blocking](docs/shadowsocks.md)
1. **Start the server**
```sh
npm run action shadowbox/server/start
./task shadowbox:start
```
Exploring further options:
@ -52,5 +53,5 @@ See [Shadowsocks resistance against detection and blocking](docs/shadowsocks.md)
1. **To clean up**
```sh
npm run clean
./task clean
```

53
Taskfile.yml Normal file
View file

@ -0,0 +1,53 @@
version: '3'
set: [pipefail]
run: when_changed
vars:
REPO_ROOT: "{{.ROOT_DIR}}"
BUILD_ROOT: "{{.ROOT_DIR}}/build"
DOCKER: '{{.DOCKER | default "docker"}}'
includes:
metrics_server:
taskfile: ./src/metrics_server/Taskfile.yml
vars: {OUTPUT_BASE: '{{joinPath .BUILD_ROOT "metrics_server"}}'}
sentry_webhook:
taskfile: ./src/sentry_webhook/Taskfile.yml
vars: {OUTPUT_BASE: '{{joinPath .BUILD_ROOT "sentry_webhook"}}'}
shadowbox:
taskfile: ./src/shadowbox/Taskfile.yml
vars: {OUTPUT_BASE: '{{joinPath .BUILD_ROOT "shadowbox"}}'}
tasks:
clean:
desc: Clean output files
cmds:
- rm -rf .task task src/*/node_modules/ build/ node_modules/ third_party/shellcheck/download/ third_party/*/bin third_party/jsign/*.jar
format:
desc: Format staged files
cmds: ['npx pretty-quick --staged --pattern "**/*.{cjs,html,js,json,md,ts}"']
format:all:
desc: Format all files in the repository
cmds: ['npx prettier --write "**/*.{cjs,html,js,json,md,ts}"']
lint:
desc: Lint all files
deps: [lint:sh, lint:ts]
lint:sh:
desc: Lint all shell files
cmds: [bash ./scripts/shellcheck.sh]
lint:ts:
desc: Lint all .ts and .js files
cmds: ['npx eslint "**/*.{js,ts}"']
test:
desc: Run all the repository tests
deps: [lint, metrics_server:test, sentry_webhook:test, shadowbox:test]

46
go.mod Normal file
View file

@ -0,0 +1,46 @@
module localhost
go 1.21
require (
github.com/Jigsaw-Code/outline-ss-server v1.5.0
github.com/go-task/task/v3 v3.36.0
)
require (
github.com/Jigsaw-Code/outline-sdk v0.0.14 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-zglob v0.0.4 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect
github.com/oschwald/geoip2-golang v1.8.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/prometheus/client_golang v1.15.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/radovskyb/watcher v1.0.7 // indirect
github.com/sajari/fuzzy v1.0.0 // indirect
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
mvdan.cc/sh/v3 v3.8.0 // indirect
)

117
go.sum Normal file
View file

@ -0,0 +1,117 @@
github.com/Jigsaw-Code/outline-sdk v0.0.14 h1:uJLvIne7YJNolbX7KDacd8gLidrUzRuweBO2APmQEmI=
github.com/Jigsaw-Code/outline-sdk v0.0.14/go.mod h1:9cEaF6sWWMzY8orcUI9pV5D0oFp2FZArTSyJiYtMQQs=
github.com/Jigsaw-Code/outline-ss-server v1.5.0 h1:Vz+iS0xR7i3PrLD82pzFFwZ9fsh6zrNawMeYERR8VTc=
github.com/Jigsaw-Code/outline-ss-server v1.5.0/go.mod h1:KaebwBiCWDSkgsJrJIbGH0szON8CZq4LgQaFV8v3RM4=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-task/task/v3 v3.36.0 h1:XVJ5hQ5hdzTAulHpAGzbUMUuYr9MUOEQFOFazI3hUsY=
github.com/go-task/task/v3 v3.36.0/go.mod h1:XBCIAzuyG/mgZVHMUm3cCznz4+IpsBQRlW1gw7OA5sA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM=
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8=
mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY=

View file

@ -24,27 +24,18 @@
"pretty-quick": "^3.1.1",
"typescript": "^4.9.5"
},
"scripts": {
"postinstall": "go build github.com/go-task/task/v3/cmd/task"
},
"engines": {
"node": "18.x.x"
},
"scripts": {
"action": "bash ./scripts/run_action.sh",
"action:help": "npm run action",
"action:list": "npm run action",
"clean": "rm -rf src/*/node_modules/ build/ node_modules/ src/server_manager/install_scripts/do_install_script.ts src/server_manager/install_scripts/gcp_install_script.ts third_party/shellcheck/download/ third_party/*/bin third_party/jsign/*.jar",
"format": "pretty-quick --staged --pattern \"**/*.{cjs,html,js,json,md,ts}\"",
"format:all": "prettier --write \"**/*.{cjs,html,js,json,md,ts}\"",
"lint": "npm run lint:sh && npm run lint:ts",
"lint:sh": "bash ./scripts/shellcheck.sh",
"lint:ts": "eslint \"**/*.{js,ts}\"",
"test": "npm run lint && npm run action metrics_server/test && npm run action sentry_webhook/build && npm run action server_manager/test && npm run action shadowbox/test"
},
"workspaces": [
"src/*"
],
"husky": {
"hooks": {
"pre-commit": "npm run lint && npm run format"
"pre-commit": "./task lint format"
}
}
}

View file

@ -1,67 +0,0 @@
#!/bin/bash
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eu
# TODO: Because Node.js on Cygwin doesn't handle absolute paths very
# well, it would be worth pushd-ing to ROOT_DIR before invoking
# them and making BUILD_DIR a relative path, viz. just "build".
readonly ROOT_DIR=${ROOT_DIR:-$(pwd)/$(git rev-parse --show-cdup)}
readonly BUILD_DIR=${BUILD_DIR:-${ROOT_DIR}/build}
export ROOT_DIR
export BUILD_DIR
export run_action_indent=''
function run_action() {
local -r STYLE_BOLD_WHITE='\033[1;37m'
local -r STYLE_BOLD_GREEN='\033[1;32m'
local -r STYLE_BOLD_RED='\033[1;31m'
local -r STYLE_RESET='\033[0m'
local -r action="${1:-""}"
local -r old_indent="${run_action_indent}"
run_action_indent="=> ${run_action_indent}"
if [[ -z "${action}" ]]; then
echo -e "Please provide an action to run. ${STYLE_BOLD_WHITE}List of valid actions:${STYLE_RESET}\n"
find . -name '*.action.sh' | sed -E 's:\./src/(.*)\.action\.sh:\1:'
exit 0
fi
echo -e "${old_indent}${STYLE_BOLD_WHITE}[Running ${action}]${STYLE_RESET}"
shift
"${ROOT_DIR}src/${action}.action.sh" "$@"
local -ir status="$?"
if (( status == 0 )); then
echo -e "${old_indent}${STYLE_BOLD_GREEN}[${action}: Finished]${STYLE_RESET}"
else
echo -e "${old_indent}${STYLE_BOLD_RED}[${action}: Failed]${STYLE_RESET}"
fi
run_action_indent="${old_indent}"
return "${status}"
}
export -f run_action
run_action "$@"

View file

@ -48,7 +48,7 @@ The metrics server supports two URL paths:
## Build
```sh
npm run action metrics_server/build
task metrics_server:build
```
## Run
@ -56,7 +56,7 @@ npm run action metrics_server/build
Run a local development metrics server:
```sh
npm run action metrics_server/start
task metrics_server:start
```
## Deploy
@ -67,20 +67,20 @@ npm run action metrics_server/start
```
- To deploy to dev:
```sh
npm run action metrics_server/deploy_dev
task metrics_server:deploy:dev
```
- To deploy to prod:
```sh
npm run action metrics_server/deploy_prod
task metrics_server:deploy:prod
```
## Test
- Unit test
```sh
npm run action metrics_server/test
task metrics_server:test
```
- Integration test
```sh
npm run action metrics_server/test_integration
task metrics_server:integration_test
```

View file

@ -0,0 +1,65 @@
version: '3'
requires:
vars: [OUTPUT_BASE]
tasks:
clean:
desc: Clean metrics server output
cmds:
- rm -rf "{{.OUTPUT_BASE}}"
build:
desc: Build the metrics server
vars:
BUILD_MODE: '{{.BUILD_MODE | default "dev"}}'
TARGET_DIR: &default-target-dir '{{joinPath .OUTPUT_BASE .BUILD_MODE}}'
cmds:
- rm -rf '{{.TARGET_DIR}}'
- npx tsc --project '{{.TASKFILE_DIR}}' --outDir '{{.TARGET_DIR}}'
- cp '{{joinPath .TASKFILE_DIR "package.json"}}' '{{.TARGET_DIR}}'
- cp '{{joinPath .USER_WORKING_DIR "package-lock.json"}}' '{{.TARGET_DIR}}'
- cp '{{.TASKFILE_DIR}}/app_{{.BUILD_MODE}}.yaml' '{{.TARGET_DIR}}/app.yaml'
- cp '{{.TASKFILE_DIR}}/config_{{.BUILD_MODE}}.json' '{{.TARGET_DIR}}/config.json'
deploy:dev:
desc: Deploy the development metrics server
vars:
BUILD_MODE: "dev"
TARGET_DIR: *default-target-dir
deps: [{task: build, vars: {BUILD_MODE: "{{.BUILD_MODE}}", TARGET_DIR: "{{.TARGET_DIR}}"}}]
cmds:
- gcloud app deploy '{{.TASKFILE_DIR}}/dispatch.yaml' '{{.TARGET_DIR}}' --project uproxysite --verbosity info --promote --stop-previous-version
deploy:prod:
desc: Deploy the production metrics server
vars:
BUILD_MODE: "prod"
TARGET_DIR: *default-target-dir
deps: [{task: build, vars: {BUILD_MODE: "{{.BUILD_MODE}}", TARGET_DIR: "{{.TARGET_DIR}}"}}]
cmds:
- gcloud app deploy '{{.TASKFILE_DIR}}/dispatch.yaml' '{{joinPath .OUTPUT_BASE "prod"}}' --project uproxysite --verbosity info --no-promote --no-stop-previous-version
start:
desc: Start the metrics server locally
vars:
BUILD_MODE: '{{.BUILD_MODE | default "dev"}}'
TARGET_DIR: *default-target-dir
deps: [{task: build, vars: {BUILD_MODE: "{{.BUILD_MODE}}", TARGET_DIR: "{{.TARGET_DIR}}"}}]
cmds:
- node '{{joinPath .TARGET_DIR "index.js"}}'
integration_test:
desc: Test the deployed dev metrics server
cmds:
- '{{.TASKFILE_DIR}}/test_integration.sh'
test:
desc: Run the unit tests for the metrics server
vars:
TEST_DIR:
sh: "mktemp -d"
cmds:
- defer: rm -rf "{{.TEST_DIR}}"
- npx tsc -p '{{.TASKFILE_DIR}}' --outDir '{{.TEST_DIR}}'
- npx jasmine '{{.TEST_DIR}}/**/*.spec.js'

View file

@ -1,17 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
tsc -p "$(dirname "$0")"

View file

@ -1,29 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly SRC_DIR="src/metrics_server"
readonly BUILD_DIR="build/metrics_server"
rm -rf "${BUILD_DIR}"
npm run action metrics_server/build
cp "${SRC_DIR}/app_dev.yaml" "${BUILD_DIR}/app.yaml"
cp "${SRC_DIR}/config_dev.json" "${BUILD_DIR}/config.json"
cp "${SRC_DIR}/package.json" "${BUILD_DIR}/"
cp "./package-lock.json" "${BUILD_DIR}/"
gcloud app deploy "${SRC_DIR}/dispatch.yaml" "${BUILD_DIR}" --project uproxysite --verbosity info --promote --stop-previous-version

View file

@ -1,29 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly SRC_DIR="src/metrics_server"
readonly BUILD_DIR="build/metrics_server"
rm -rf "${BUILD_DIR}"
npm run action metrics_server/build
cp "${SRC_DIR}/app_prod.yaml" "${BUILD_DIR}/app.yaml"
cp "${SRC_DIR}/config_prod.json" "${BUILD_DIR}/config.json"
cp "${SRC_DIR}/package.json" "${BUILD_DIR}/"
cp "./package-lock.json" "${BUILD_DIR}/"
gcloud app deploy "${SRC_DIR}/dispatch.yaml" "${BUILD_DIR}" --project uproxysite --verbosity info --no-promote --no-stop-previous-version

View file

@ -1,25 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2020 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly SRC_DIR="src/metrics_server"
readonly BUILD_DIR="build/metrics_server"
npm run action metrics_server/build
cp "${SRC_DIR}/config_dev.json" "${BUILD_DIR}/config.json"
cp "${SRC_DIR}/package.json" "${BUILD_DIR}/"
npx node "${BUILD_DIR}/index.js"

View file

@ -1,23 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly TEST_DIR="${BUILD_DIR}/js/metrics_server/"
rm -rf "${TEST_DIR}"
tsc -p "${ROOT_DIR}/src/metrics_server" --outDir "${TEST_DIR}"
jasmine --config="${ROOT_DIR}/jasmine.json"
rm -rf "${TEST_DIR}"

View file

@ -10,7 +10,7 @@ The Outline Sentry webhook is a [Google Cloud Function](https://cloud.google.com
## Build
```sh
npm run action sentry_webhook/build
task sentry_webhook:build
```
## Deploy
@ -24,7 +24,7 @@ gcloud auth login
To deploy:
```sh
npm run action sentry_webhook/deploy
task sentry_webhook:deploy
```
## Configure Sentry Webhooks

View file

@ -0,0 +1,39 @@
version: '3'
requires:
vars: [OUTPUT_BASE]
tasks:
clean:
desc: Clean Sentry webhook output
cmds:
- rm -rf "{{.OUTPUT_BASE}}"
build:
desc: Build the Sentry webhook
cmds:
- npx tsc --project '{{.TASKFILE_DIR}}/tsconfig.prod.json' --outDir '{{.OUTPUT_BASE}}'
- cp '{{.TASKFILE_DIR}}/package.json' '{{.OUTPUT_BASE}}'
deploy:
desc: Deploy the Sentry webhook to GCP Cloud Functions
deps: [build]
cmds:
- gcloud functions deploy postSentryEventToSalesforce
--project=uproxysite
--runtime=nodejs18
--trigger-http
--source='{{.OUTPUT_BASE}}'
--entry-point=postSentryEventToSalesforce
test:
desc: Run the unit tests for the Sentry webhook
vars:
TEST_DIR:
sh: "mktemp -d"
cmds:
- defer: rm -rf "{{.TEST_DIR}}"
# Use commonjs modules, jasmine runs in node.
- npx tsc -p '{{.TASKFILE_DIR}}' --outDir '{{.TEST_DIR}}' --module commonjs
- npx jasmine '{{.TEST_DIR}}/**/*.spec.js'
- npx karma start '{{.TASKFILE_DIR}}/karma.conf.js'

View file

@ -1,17 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
tsc --project "${ROOT_DIR}"src/sentry_webhook/tsconfig.prod.json

View file

@ -1,25 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
npm run action sentry_webhook/build
cp src/sentry_webhook/package.json build/sentry_webhook/
gcloud functions deploy postSentryEventToSalesforce \
--project=uproxysite \
--runtime=nodejs18 \
--trigger-http \
--source=build/sentry_webhook \
--entry-point=postSentryEventToSalesforce

View file

@ -1,26 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2023 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly TEST_DIR="${BUILD_DIR}/js/sentry_webhook/"
rm -rf "${TEST_DIR}"
# Use commonjs modules, jasmine runs in node.
tsc -p "${ROOT_DIR}/src/sentry_webhook" --outDir "${TEST_DIR}" --module commonjs
jasmine --config="${ROOT_DIR}/jasmine.json"
npx karma start "${ROOT_DIR}/src/sentry_webhook/karma.conf.js"
rm -rf "${TEST_DIR}"

View file

@ -4,7 +4,6 @@
"removeComments": false,
"strict": true,
"module": "commonjs",
"outDir": "../../build/sentry_webhook",
"skipLibCheck": true
},
"include": ["**/*.ts"]

View file

@ -43,13 +43,13 @@ The Outline Server, internal name "Shadowbox," is designed to streamline the set
- **Node.js App**
```sh
npm run action shadowbox/server/start
task shadowbox:start
```
- **Docker Container**
```sh
npm run action shadowbox/docker/start
task shadowbox:docker:start
```
> [!TIP]
@ -119,38 +119,25 @@ The Outline Server provides a REST API for access key management. If you know th
### Manual
1. Prerequisites
Build and run your image with:
- A locally built Docker image containing your modifications.
- Your Docker image uploaded to a registry (e.g., Docker Hub, Quay.io).
```sh
task shadowbox:docker:start
```
1. Setup
### Integration Test
- **Environment Variable:** Set `SB_IMAGE` to the uploaded image location. Example:
The integration test will not only build and run your image, but also run a number of automated tests.
```sh
export SB_IMAGE=yourdockerhubusername/shadowbox
```
```sh
task shadowbox:integration_test
```
- **Start the Server:**
This does the following:
```sh
npm run action shadowbox/docker/start
```
### Automated
1. **Build and Run:**
```sh
npm run action shadowbox/integration_test/run
```
This does the following:
- Sets up three containers (`client`, `shadowbox`, `target`) and two networks.
- Creates a user on `shadowbox`.
- Connects to `target` through `shadowbox` using a Shadowsocks `client`: `client <-> shadowbox <-> target`
- Sets up three containers (`client`, `shadowbox`, `target`) and two networks.
- Creates a user on `shadowbox`.
- Connects to `target` through `shadowbox` using a Shadowsocks `client`: `client <-> shadowbox <-> target`
1. **Testing Changes to the Server Config:**

198
src/shadowbox/Taskfile.yml Normal file
View file

@ -0,0 +1,198 @@
version: '3'
requires:
vars: [OUTPUT_BASE]
tasks:
build:
desc: Build the Outline Server Node.js app
vars:
TARGET_OS: '{{.TARGET_OS | default "linux"}}'
TARGET_ARCH: '{{.TARGET_ARCH | default "x86_64"}}'
GOOS: '{{get (dict "macos" "darwin") .TARGET_OS | default .TARGET_OS}}'
GOARCH: '{{get (dict "x86_64" "amd64") .TARGET_ARCH | default .TARGET_ARCH}}'
TARGET_DIR: '{{.TARGET_DIR | default (joinPath .OUTPUT_BASE .TARGET_OS .TARGET_ARCH)}}'
NODE_DIR: '{{joinPath .TARGET_DIR "app"}}'
BIN_DIR: '{{joinPath .TARGET_DIR "bin"}}'
cmds:
- echo Target platform is {{.TARGET_OS}}/{{.TARGET_ARCH}}
- rm -rf '{{.TARGET_DIR}}'
- mkdir -p '{{.TARGET_DIR}}'
- cp '{{joinPath .TASKFILE_DIR "package.json"}}' '{{.TARGET_DIR}}'
# Build Node.js app
- npx webpack --config='{{joinPath .TASKFILE_DIR "webpack.config.js"}}' --output-path='{{.NODE_DIR}}' ${BUILD_ENV:+--mode="${BUILD_ENV}"}
# Copy third_party dependencies
- mkdir -p '{{.BIN_DIR}}'
- |
{
cd '{{joinPath .USER_WORKING_DIR "third_party" "prometheus"}}'
make 'bin/{{.TARGET_OS}}-{{.TARGET_ARCH}}/prometheus'
cp 'bin/{{.TARGET_OS}}-{{.TARGET_ARCH}}/prometheus' '{{.BIN_DIR}}/'
}
# Set CGO_ENABLED=0 to force static linkage. See https://mt165.co.uk/blog/static-link-go/.
- GOOS={{.GOOS}} GOARCH={{.GOARCH}} CGO_ENABLED=0 go build -ldflags='-s -w -X main.version=embedded' -o '{{.BIN_DIR}}/' github.com/Jigsaw-Code/outline-ss-server/cmd/outline-ss-server
start:
desc: Run the Outline server locally
deps: [{task: build, vars: {TARGET_OS: '{{.TARGET_OS}}', TARGET_ARCH: '{{.TARGET_ARCH}}'}}]
vars:
UNAME_OS: {sh: 'uname -s'}
TARGET_OS: '{{get (dict "Linux" "linux" "Darwin" "macos") .UNAME_OS}}'
TARGET_ARCH: {sh: 'uname -m'}
RUN_ID: '{{.RUN_ID | default (now | date "2006-01-02-150405")}}'
RUN_DIR: '{{joinPath "/tmp/outline" .RUN_ID}}'
STATE_DIR: '{{joinPath .RUN_DIR "persisted-state"}}'
STATE_CONFIG: '{{joinPath .STATE_DIR "shadowbox_server_config.json"}}'
LOG_LEVEL: '{{.LOG_LEVEL | default "debug"}}'
env:
# WARNING: The SB_API_PREFIX should be kept secret!
SB_API_PREFIX: TestApiPrefix
SB_METRICS_URL: https://dev.metrics.getoutline.org
SB_STATE_DIR: '{{.STATE_DIR}}'
SB_PUBLIC_IP: localhost
SB_CERTIFICATE_FILE: '{{joinPath .RUN_DIR "/shadowbox-selfsigned-dev.crt"}}'
SB_PRIVATE_KEY_FILE: '{{joinPath .RUN_DIR "/shadowbox-selfsigned-dev.key"}}'
cmds:
- echo Target platform is {{.TARGET_OS}}/{{.TARGET_ARCH}}
- echo "Using directory {{.RUN_DIR}}"
- mkdir -p '{{.STATE_DIR}}'
- echo '{"hostname":"127.0.0.1"}' > "{{.STATE_CONFIG}}"
- task: make_test_certificate
vars: {OUTPUT_DIR: '{{.RUN_DIR}}'}
- node '{{joinPath .OUTPUT_BASE .TARGET_OS .TARGET_ARCH "app/main.js"}}'
docker:build:
desc: Build the Outline Server Docker image
vars:
VERSION: '{{.IMAGE_VERSION | default "dev"}}'
IMAGE_NAME: '{{.IMAGE_NAME | default "localhost/outline/shadowbox"}}'
TARGET_ARCH: '{{.TARGET_ARCH | default "x86_64"}}'
IMAGE_ROOT: '{{joinPath .OUTPUT_BASE "image_root" .TARGET_ARCH}}'
# Newer node images have no valid content trust data.
# Pin the image node:16.18.0-alpine3.16 by hash.
# See image at https://hub.docker.com/_/node/tags?page=1&name=18.18.0-alpine3.18
NODE_IMAGE: '{{get
(dict
"x86_64" "node@sha256:a0b787b0d53feacfa6d606fb555e0dbfebab30573277f1fe25148b05b66fa097"
"arm64" "node@sha256:b4b7a1dd149c65ee6025956ac065a843b4409a62068bd2b0cbafbb30ca2fab3b"
) .TARGET_ARCH
}}'
env:
DOCKER_CONTENT_TRUST: '{{.DOCKER_CONTENT_TRUST | default "1"}}'
# Enable Docker BuildKit (https://docs.docker.com/develop/develop-images/build_enhancements)
DOCKER_BUILDKIT: 1
cmds:
- rm -rf '{{.IMAGE_ROOT}}'
- mkdir -p '{{.IMAGE_ROOT}}'
- {task: build, vars: {TARGET_OS: linux, TARGET_ARCH: '{{.TARGET_ARCH}}', TARGET_DIR: '{{joinPath .IMAGE_ROOT "/opt/outline-server"}}'}}
- cp -R '{{joinPath .TASKFILE_DIR "scripts"}}' '{{.IMAGE_ROOT}}/scripts'
- mkdir -p '{{joinPath .IMAGE_ROOT "/etc/periodic/weekly"}}'
- cp '{{joinPath .TASKFILE_DIR "scripts" "update_mmdb.sh"}}' '{{joinPath .IMAGE_ROOT "/etc/periodic/weekly/"}}'
# Create default state directory
- mkdir -p '{{joinPath .IMAGE_ROOT "/root/shadowbox/persisted-state"}}'
# Copy entrypoint command
- cp '{{joinPath .TASKFILE_DIR "docker/cmd.sh"}}' '{{.IMAGE_ROOT}}/'
# Build image with given root
- |
"${DOCKER:-docker}" build --force-rm \
--build-arg NODE_IMAGE='{{.NODE_IMAGE}}' \
--build-arg VERSION='{{.VERSION}}' \
-f '{{joinPath .TASKFILE_DIR "docker" "Dockerfile"}}' \
-t '{{.IMAGE_NAME}}' \
'{{.IMAGE_ROOT}}'
docker:start:
desc: Build and run the Outline Server Docker image
interactive: true
requires:
vars: [DOCKER]
deps: [{task: docker:build, vars: {TARGET_ARCH: {sh: 'uname -m'}}}]
vars:
RUN_DIR: '{{joinPath .OUTPUT_BASE "docker_start"}}'
IMAGE_NAME: '{{.IMAGE_NAME | default "localhost/outline/shadowbox"}}'
API_PORT: '8081'
ACCESS_KEY_PORT: '9999'
CERTIFICATE_FILE: 'shadowbox-selfsigned-dev.crt'
PRIVATE_KEY_FILE: 'shadowbox-selfsigned-dev.key'
HOST_STATE_DIR: '{{joinPath .RUN_DIR "persisted-state"}}'
CONTAINER_STATE_DIR: '/opt/outline/pesisted-state'
STATE_CONFIG: '{{joinPath .HOST_STATE_DIR "shadowbox_server_config.json"}}'
cmds:
- rm -rf '{{.RUN_DIR}}'
- mkdir -p '{{.HOST_STATE_DIR}}'
- echo '{"hostname":"127.0.0.1"}' > "{{.STATE_CONFIG}}"
- task: make_test_certificate
vars: {OUTPUT_DIR: '{{.HOST_STATE_DIR}}'}
- |
docker_command=(
'{{.DOCKER}}' run -it --rm --name 'shadowbox'
{{if eq OS "linux" -}}
--net host
{{else}}
-p '{{.API_PORT}}:{{.API_PORT}}'
-p '{{.ACCESS_KEY_PORT}}:{{.ACCESS_KEY_PORT}}'
-p '{{.ACCESS_KEY_PORT}}:{{.ACCESS_KEY_PORT}}/udp'
-p '9090-9092:9090-9092'
{{- end}}
# Where the container keeps its persistent state.
-v "{{.HOST_STATE_DIR}}:{{.CONTAINER_STATE_DIR}}"
-e "SB_STATE_DIR={{.CONTAINER_STATE_DIR}}"
# Port number and path prefix used by the server manager API.
-e "SB_API_PORT={{.API_PORT}}"
-e "SB_API_PREFIX=TestApiPrefix"
# Location of the API TLS certificate and key.
-e "SB_CERTIFICATE_FILE={{joinPath .CONTAINER_STATE_DIR .CERTIFICATE_FILE}}"
-e "SB_PRIVATE_KEY_FILE={{joinPath .CONTAINER_STATE_DIR .PRIVATE_KEY_FILE}}"
# Where to report metrics to, if opted-in.
-e "SB_METRICS_URL={{.METRICS_URL | default "https://dev.metrics.getoutline.org"}}"
# The Outline server image to run.
'{{.IMAGE_NAME}}'
)
"${docker_command[@]}"
integration_test:
desc: Run the integration test
cmds:
- task: docker:build
- task: test_image
vars:
IMAGE_NAME: localhost/outline/shadowbox:latest
OUTPUT_DIR: '{{joinPath .OUTPUT_BASE "integration_test"}}'
test_image:
desc: Test a specific image by name
requires:
vars: [IMAGE_NAME]
vars:
OUTPUT_DIR: '{{joinPath .OUTPUT_BASE "image_test"}}'
cmds:
- rm -rf '{{.OUTPUT_DIR}}'
- OUTPUT_DIR='{{.OUTPUT_DIR}}' '{{joinPath .TASKFILE_DIR "integration_test/test.sh"}}' '{{.IMAGE_NAME}}'
test:
desc: Run the unit tests for the Outline Server
vars:
TEST_DIR: '{{joinPath .OUTPUT_BASE "test"}}'
cmds:
- defer: rm -rf "{{.TEST_DIR}}"
- npx tsc -p '{{.TASKFILE_DIR}}' --outDir '{{.TEST_DIR}}'
- npx jasmine '{{.TEST_DIR}}/**/*.spec.js'
make_test_certificate:
internal: true
requires: {vars: [OUTPUT_DIR]}
vars:
CERTIFICATE_FILE: '{{joinPath .OUTPUT_DIR "shadowbox-selfsigned-dev.crt"}}'
PRIVATE_KEY_FILE: '{{joinPath .OUTPUT_DIR "shadowbox-selfsigned-dev.key"}}'
cmds:
- mkdir -p '{{.OUTPUT_DIR}}'
- >
openssl req -x509 -nodes -days 36500 -newkey rsa:4096
-subj "/CN=localhost"
-keyout "{{.PRIVATE_KEY_FILE}}" -out "{{.CERTIFICATE_FILE}}"

View file

@ -13,32 +13,8 @@
# limitations under the License.
ARG NODE_IMAGE
# Multi-stage build: use a build image to prevent bloating the shadowbox image with dependencies.
# Run `npm ci` and build inside the container to package the right dependencies for the image.
FROM ${NODE_IMAGE} AS build
# make for building third_party/prometheus and perl-utils for shasum.
RUN apk add --no-cache --upgrade bash make perl-utils
WORKDIR /
# Don't copy node_modules and other things not needed for install.
COPY package.json package-lock.json ./
COPY src/shadowbox/package.json src/shadowbox/
RUN --mount=type=cache,target=/root/.npm npm ci
# We copy the source code only after npm ci, so that source code changes don't trigger re-installs.
COPY scripts scripts/
COPY src src/
COPY tsconfig.json ./
COPY third_party third_party
ARG ARCH
ARG VERSION
RUN ARCH=${ARCH} ROOT_DIR=/ SB_VERSION=${VERSION} npm run action shadowbox/server/build
# shadowbox image
FROM ${NODE_IMAGE}
# Save metadata on the software versions we are using.
@ -53,22 +29,11 @@ STOPSIGNAL SIGKILL
# safely grab the ip-to-country database.
RUN apk add --no-cache --upgrade coreutils curl
COPY src/shadowbox/scripts scripts/
COPY src/shadowbox/scripts/update_mmdb.sh /etc/periodic/weekly/update_mmdb
COPY . /
RUN /etc/periodic/weekly/update_mmdb
# Create default state directory.
RUN mkdir -p /root/shadowbox/persisted-state
RUN /etc/periodic/weekly/update_mmdb.sh
# Install shadowbox.
WORKDIR /opt/outline-server
# The shadowbox build directory has the following structure:
# - app/ (bundled node app)
# - bin/ (binary dependencies)
# - package.json (shadowbox package.json)
COPY --from=build /build/shadowbox/ .
COPY src/shadowbox/docker/cmd.sh /
CMD /cmd.sh

View file

@ -1,51 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Environment inputs:
# - SB_VERSION
# - SB_IMAGE
# - ARCH
# - NODE_IMAGE
# - ROOT_DIR
export DOCKER_CONTENT_TRUST="${DOCKER_CONTENT_TRUST:-1}"
# Enable Docker BuildKit (https://docs.docker.com/develop/develop-images/build_enhancements)
export DOCKER_BUILDKIT=1
# Docker image build architecture. Supported architectures: x86_64, arm64
export ARCH=${ARCH:-x86_64}
# Newer node images have no valid content trust data.
# Pin the image node:16.18.0-alpine3.16 by hash.
# See image at https://hub.docker.com/_/node/tags?page=1&name=18.18.0-alpine3.18
readonly NODE_IMAGE=$(
if [[ "${ARCH}" == "x86_64" ]]; then
echo "node@sha256:a0b787b0d53feacfa6d606fb555e0dbfebab30573277f1fe25148b05b66fa097"
elif [[ "${ARCH}" == "arm64" ]]; then
echo "node@sha256:b4b7a1dd149c65ee6025956ac065a843b4409a62068bd2b0cbafbb30ca2fab3b"
else
echo "Unsupported architecture"
exit 1
fi
)
docker build --force-rm \
--build-arg ARCH="${ARCH}" \
--build-arg NODE_IMAGE="${NODE_IMAGE}" \
--build-arg VERSION="${SB_VERSION:-dev}" \
-f src/shadowbox/docker/Dockerfile \
-t "${SB_IMAGE:-localhost/outline/shadowbox}" \
"${ROOT_DIR}"

View file

@ -1,60 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
run_action shadowbox/docker/build
RUN_ID="${RUN_ID:-$(date +%Y-%m-%d-%H%M%S)}"
readonly RUN_ID
readonly RUN_DIR="/tmp/outline/${RUN_ID}"
echo "Using directory ${RUN_DIR}"
readonly HOST_STATE_DIR="${RUN_DIR}/persisted-state"
readonly CONTAINER_STATE_DIR='/root/shadowbox/persisted-state'
readonly STATE_CONFIG="${HOST_STATE_DIR}/shadowbox_server_config.json"
declare -ir ACCESS_KEY_PORT=${ACCESS_KEY_PORT:-9999}
declare -ir SB_API_PORT=${SB_API_PORT:-8081}
[[ -d "${HOST_STATE_DIR}" ]] || mkdir -p "${HOST_STATE_DIR}"
[[ -e "${STATE_CONFIG}" ]] || echo "{\"hostname\":\"127.0.0.1\", \"portForNewAccessKeys\": ${ACCESS_KEY_PORT}}" > "${STATE_CONFIG}"
# shellcheck source=../scripts/make_test_certificate.sh
source "${ROOT_DIR}/src/shadowbox/scripts/make_test_certificate.sh" "${RUN_DIR}"
# TODO: mount a folder rather than individual files.
declare -ar docker_bindings=(
-v "${HOST_STATE_DIR}:${CONTAINER_STATE_DIR}"
-e "SB_STATE_DIR=${CONTAINER_STATE_DIR}"
-v "${SB_CERTIFICATE_FILE}:${SB_CERTIFICATE_FILE}"
-v "${SB_PRIVATE_KEY_FILE}:${SB_PRIVATE_KEY_FILE}"
-e "LOG_LEVEL=${LOG_LEVEL:-debug}"
-e "SB_API_PORT=${SB_API_PORT}"
-e "SB_API_PREFIX=TestApiPrefix"
-e "SB_CERTIFICATE_FILE=${SB_CERTIFICATE_FILE}"
-e "SB_PRIVATE_KEY_FILE=${SB_PRIVATE_KEY_FILE}"
-e "SB_METRICS_URL=${SB_METRICS_URL:-https://dev.metrics.getoutline.org}"
)
readonly IMAGE="${SB_IMAGE:-localhost/outline/shadowbox}"
echo "Running image ${IMAGE}"
declare -a NET_BINDINGS=("--network=host")
if [[ "$(uname)" == "Darwin" ]]; then
# Docker does not support the --network=host option on macOS. Instead, publish the management API
# and access key ports to the host.
NET_BINDINGS=(-p "${SB_API_PORT}:${SB_API_PORT}" -p "${ACCESS_KEY_PORT}:${ACCESS_KEY_PORT}" -p "${ACCESS_KEY_PORT}:${ACCESS_KEY_PORT}/udp")
fi;
docker run --rm -it "${NET_BINDINGS[@]}" --name shadowbox "${docker_bindings[@]}" "${IMAGE}"

View file

@ -5,11 +5,17 @@ This folder contains the integration test for the Outline Server image.
To build and test the image:
```sh
npm run action shadowbox/integration_test/run
task shadowbox:integration_test
```
For development of the test, or to test a specific image, you may prefer calling the test directly, without the build step:
```sh
./src/shadowbox/integration_test/test.sh localhost/outline/shadowbox:latest
./task shadowbox:test_image IMAGE_NAME=quay.io/outline/shadowbox:stable
```
If you prefer to use Podman instead of Docker, set the `DOCKER=podman` environment variable:
```sh
DOCKER=podman task shadowbox:integration_test
```

View file

@ -1,38 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
run_action shadowbox/docker/build
LOGFILE="$(mktemp)"
readonly LOGFILE
echo "Running Shadowbox integration test. Logs at ${LOGFILE}"
cd src/shadowbox/integration_test
declare -i result=0
if ./test.sh "${SB_IMAGE:-localhost/outline/shadowbox:latest}" > "${LOGFILE}" 2>&1 ; then
echo "Test Passed!"
# Removing the log file sometimes fails on Travis. There's no point in us cleaning it up
# on a CI build anyways.
rm -f "${LOGFILE}"
else
result=$?
echo "Test Failed! Logs:"
cat "${LOGFILE}"
fi
exit "${result}"

View file

@ -32,8 +32,12 @@
set -x
OUTPUT_DIR="$(mktemp -d)"
OUTPUT_DIR="${OUTPUT_DIR:-$(mktemp -d)}"
readonly OUTPUT_DIR
# Set DOCKER=podman to use Podman instead of Docker.
readonly DOCKER="${DOCKER:-docker}"
# TODO(fortuna): Make it possible to run multiple tests in parallel by adding a
# run id to the container names.
readonly NAMESPACE='integrationtest'
@ -62,7 +66,7 @@ function wait_for_resource() {
}
function util_jq() {
docker run --rm -i --entrypoint jq "${UTIL_IMAGE}" "$@"
"${DOCKER}" run --rm -i --entrypoint jq "${UTIL_IMAGE}" "$@"
}
# Takes the JSON from a /access-keys POST request and returns the appropriate
@ -78,7 +82,7 @@ function ss_arguments_for_user() {
# Runs curl on the client container.
function client_curl() {
docker exec "${CLIENT_CONTAINER}" curl --silent --show-error --connect-timeout 5 --retry 5 "$@"
"${DOCKER}" exec "${CLIENT_CONTAINER}" curl --silent --show-error --connect-timeout 5 --retry 5 "$@"
}
function fail() {
@ -89,12 +93,12 @@ function fail() {
function setup() {
remove_containers
docker network create -d bridge "${NET_OPEN}"
docker network create -d bridge --internal "${NET_BLOCKED}"
"${DOCKER}" network create -d bridge "${NET_OPEN}"
"${DOCKER}" network create -d bridge --internal "${NET_BLOCKED}"
# Target service.
docker build --force-rm -t "${TARGET_IMAGE}" "$(dirname "$0")/target"
docker run -d --rm -p "10080:80" --network="${NET_OPEN}" --network-alias="target" --name="${TARGET_CONTAINER}" "${TARGET_IMAGE}"
"${DOCKER}" build --force-rm -t "${TARGET_IMAGE}" "$(dirname "$0")/target"
"${DOCKER}" run -d --rm -p "10080:80" --network="${NET_OPEN}" --network-alias="target" --name="${TARGET_CONTAINER}" "${TARGET_IMAGE}"
# Shadowsocks service.
declare -ar shadowbox_flags=(
@ -110,38 +114,38 @@ function setup() {
-e "SB_PRIVATE_KEY_FILE=/root/shadowbox/test.key"
-v "${SB_CERTIFICATE_FILE}:/root/shadowbox/test.crt"
-v "${SB_PRIVATE_KEY_FILE}:/root/shadowbox/test.key"
-v "${TMP_STATE_DIR}:/root/shadowbox/persisted-state"
-v "${STATE_DIR}:/root/shadowbox/persisted-state"
--name "${SHADOWBOX_CONTAINER}"
"${SHADOWBOX_IMAGE}"
)
docker run "${shadowbox_flags[@]}"
# docker network connect --alias shadowbox "${NET_BLOCKED}" "${SHADOWBOX_CONTAINER}"
docker network connect "${NET_OPEN}" "${SHADOWBOX_CONTAINER}"
"${DOCKER}" run "${shadowbox_flags[@]}"
# "${DOCKER}" network connect --alias shadowbox "${NET_BLOCKED}" "${SHADOWBOX_CONTAINER}"
"${DOCKER}" network connect "${NET_OPEN}" "${SHADOWBOX_CONTAINER}"
# Client service.
docker build --force-rm -t "${CLIENT_IMAGE}" "$(dirname "$0")/client"
"${DOCKER}" build --force-rm -t "${CLIENT_IMAGE}" "$(dirname "$0")/client"
# Use -i to keep the container running.
docker run -d --rm -it -p "30555:555" --network "${NET_BLOCKED}" --name "${CLIENT_CONTAINER}" "${CLIENT_IMAGE}"
"${DOCKER}" run -d --rm -it --network "${NET_BLOCKED}" --name "${CLIENT_CONTAINER}" "${CLIENT_IMAGE}"
# Utilities
docker build --force-rm -t "${UTIL_IMAGE}" "$(dirname "$0")/util"
"${DOCKER}" build --force-rm -t "${UTIL_IMAGE}" "$(dirname "$0")/util"
}
function remove_containers() {
# Force remove (-f) running containers and `|| true` to not trigger a shell error
# in case the container or network doesn't exist.
docker rm -f -v "${TARGET_CONTAINER}" || true
docker rm -f -v "${SHADOWBOX_CONTAINER}" || true
docker rm -f -v "${CLIENT_CONTAINER}" || true
docker network rm "${NET_OPEN}" || true
docker network rm "${NET_BLOCKED}" || true
"${DOCKER}" rm -f -v "${TARGET_CONTAINER}" || true
"${DOCKER}" rm -f -v "${SHADOWBOX_CONTAINER}" || true
"${DOCKER}" rm -f -v "${CLIENT_CONTAINER}" || true
"${DOCKER}" network rm "${NET_OPEN}" || true
"${DOCKER}" network rm "${NET_BLOCKED}" || true
}
function cleanup() {
local -i status=$?
if ((DEBUG != 1)); then
remove_containers
rm -rf "${TMP_STATE_DIR}" || echo "Failed to cleanup files at ${TMP_STATE_DIR}"
rm -rf "${STATE_DIR}" || echo "Failed to cleanup files at ${STATE_DIR}"
fi
return "${status}"
}
@ -157,33 +161,33 @@ function cleanup() {
# Sets everything up
export SB_API_PREFIX='TestApiPrefix'
readonly SB_API_URL="https://shadowbox/${SB_API_PREFIX}"
TMP_STATE_DIR="$(mktemp -d)"
export TMP_STATE_DIR
echo '{"hostname": "shadowbox"}' > "${TMP_STATE_DIR}/shadowbox_server_config.json"
export STATE_DIR="${OUTPUT_DIR}/container_state"
mkdir -p "${STATE_DIR}"
echo '{"hostname": "shadowbox"}' > "${STATE_DIR}/shadowbox_server_config.json"
# Make the certificates. This exports SB_CERTIFICATE_FILE and SB_PRIVATE_KEY_FILE.
# shellcheck source=../scripts/make_test_certificate.sh
source "$(dirname "$0")/../scripts/make_test_certificate.sh" "${TMP_STATE_DIR}"
source "$(dirname "$0")/../scripts/make_test_certificate.sh" "${STATE_DIR}"
setup
# Wait for target to come up.
wait_for_resource localhost:10080
TARGET_IP="$(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${TARGET_CONTAINER}")"
wait_for_resource 127.0.0.1:10080
TARGET_IP="$("${DOCKER}" inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${TARGET_CONTAINER}")"
readonly TARGET_IP
# Verify that the client cannot access or even resolve the target
# Exit code 7 is "Failed to connect to host" and 28 is "Connection timed out".
(docker exec "${CLIENT_CONTAINER}" curl --silent --connect-timeout 5 "http://${TARGET_IP}" > /dev/null && \
("${DOCKER}" exec "${CLIENT_CONTAINER}" curl --silent --connect-timeout 5 "http://${TARGET_IP}" > /dev/null && \
fail "Client should not have access to target IP") || (($? == 7 || $? == 28))
# Exit code 6 for "Could not resolve host". In some environments, curl reports a timeout
# error (28) instead, which is surprising. TODO: Investigate and fix.
(docker exec "${CLIENT_CONTAINER}" curl --silent --connect-timeout 5 http://target > /dev/null && \
("${DOCKER}" exec "${CLIENT_CONTAINER}" curl --silent --connect-timeout 5 http://target > /dev/null && \
fail "Client should not have access to target host") || (($? == 6 || $? == 28))
# Wait for shadowbox to come up.
wait_for_resource https://localhost:20443/access-keys
wait_for_resource https://127.0.0.1:20443/access-keys
# Verify that the shadowbox can access the target
docker exec "${SHADOWBOX_CONTAINER}" wget --spider http://target
"${DOCKER}" exec "${SHADOWBOX_CONTAINER}" wget --spider http://target
# Create new shadowbox user.
# TODO(bemasc): Verify that the server is using the right certificate
@ -199,27 +203,27 @@ function cleanup() {
# Start Shadowsocks client and wait for it to be ready
declare -ir LOCAL_SOCKS_PORT=5555
docker exec -d "${CLIENT_CONTAINER}" \
/go/bin/go-shadowsocks2 "${SS_USER_ARGUMENTS[@]}" -socks "localhost:${LOCAL_SOCKS_PORT}" -verbose \
"${DOCKER}" exec -d "${CLIENT_CONTAINER}" \
/go/bin/go-shadowsocks2 "${SS_USER_ARGUMENTS[@]}" -socks "127.0.0.1:${LOCAL_SOCKS_PORT}" -verbose \
|| fail "Could not start shadowsocks client"
while ! docker exec "${CLIENT_CONTAINER}" nc -z localhost "${LOCAL_SOCKS_PORT}"; do
while ! "${DOCKER}" exec "${CLIENT_CONTAINER}" nc -z 127.0.0.1 "${LOCAL_SOCKS_PORT}"; do
sleep 0.1
done
function test_networking() {
# Verify the server blocks requests to hosts on private addresses.
# Exit code 52 is "Empty server response".
(client_curl -x "socks5h://localhost:${LOCAL_SOCKS_PORT}" "${TARGET_IP}" &> /dev/null \
(client_curl -x "socks5h://127.0.0.1:${LOCAL_SOCKS_PORT}" "${TARGET_IP}" \
&& fail "Target host in a private network accessible through shadowbox") || (($? == 52))
# Verify we can retrieve the internet target URL.
client_curl -x "socks5h://localhost:${LOCAL_SOCKS_PORT}" "${INTERNET_TARGET_URL}" \
client_curl -x "socks5h://127.0.0.1:${LOCAL_SOCKS_PORT}" "${INTERNET_TARGET_URL}" \
|| fail "Could not fetch ${INTERNET_TARGET_URL} through shadowbox."
# Verify we can't access the URL anymore after the key is deleted
client_curl --insecure -X DELETE "${SB_API_URL}/access-keys/0" > /dev/null
# Exit code 56 is "Connection reset by peer".
(client_curl -x "socks5h://localhost:${LOCAL_SOCKS_PORT}" "${INTERNET_TARGET_URL}" &> /dev/null \
(client_curl -x "socks5h://127.0.0.1:${LOCAL_SOCKS_PORT}" "${INTERNET_TARGET_URL}" &> /dev/null \
&& fail "Deleted access key is still active") || (($? == 56))
}
@ -319,7 +323,7 @@ function cleanup() {
# Verify no errors occurred.
readonly SHADOWBOX_LOG="${OUTPUT_DIR}/shadowbox-log.txt"
if docker logs "${SHADOWBOX_CONTAINER}" 2>&1 | tee "${SHADOWBOX_LOG}" | grep -Eq "^E|level=error|ERROR:"; then
if "${DOCKER}" logs "${SHADOWBOX_CONTAINER}" 2>&1 | tee "${SHADOWBOX_LOG}" | grep -Eq "^E|level=error|ERROR:"; then
cat "${SHADOWBOX_LOG}"
fail "Found errors in Shadowbox logs (see above, also saved to ${SHADOWBOX_LOG})"
fi

View file

@ -12,6 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
FROM alpine:3.5
FROM alpine:3.19.1
RUN apk add --no-cache jq
ENTRYPOINT [ "sh" ]

View file

@ -1,40 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly OUT_DIR="${BUILD_DIR}/shadowbox"
rm -rf "${OUT_DIR}"
mkdir -p "${OUT_DIR}"
webpack --config=src/shadowbox/webpack.config.js ${BUILD_ENV:+--mode="${BUILD_ENV}"}
# Install third_party dependencies
readonly OS="$([[ "$(uname)" == "Darwin" ]] && echo "macos" || echo "linux")"
export ARCH=${ARCH:-x86_64}
readonly BIN_DIR="${OUT_DIR}/bin"
mkdir -p "${BIN_DIR}"
{
cd "${ROOT_DIR}/third_party/prometheus"
make "bin/${OS}-${ARCH}/prometheus"
cp "bin/${OS}-${ARCH}/prometheus" "${BIN_DIR}/"
}
{
cd "${ROOT_DIR}/third_party/outline-ss-server"
make "bin/${OS}-${ARCH}/outline-ss-server"
cp "bin/${OS}-${ARCH}/outline-ss-server" "${BIN_DIR}/"
}
# Copy shadowbox package.json
cp "${ROOT_DIR}/src/shadowbox/package.json" "${OUT_DIR}/"

View file

@ -1,38 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
run_action shadowbox/server/build
RUN_ID="${RUN_ID:-$(date +%Y-%m-%d-%H%M%S)}"
readonly RUN_DIR="/tmp/outline/${RUN_ID}"
echo "Using directory ${RUN_DIR}"
export LOG_LEVEL="${LOG_LEVEL:-debug}"
SB_PUBLIC_IP="${SB_PUBLIC_IP:-$(curl https://ipinfo.io/ip)}"
export SB_PUBLIC_IP
# WARNING: The SB_API_PREFIX should be kept secret!
export SB_API_PREFIX='TestApiPrefix'
export SB_METRICS_URL='https://dev.metrics.getoutline.org'
export SB_STATE_DIR="${RUN_DIR}/persisted-state"
readonly STATE_CONFIG="${SB_STATE_DIR}/shadowbox_server_config.json"
[[ -d "${SB_STATE_DIR}" ]] || mkdir -p "${SB_STATE_DIR}"
[[ -e "${STATE_CONFIG}" ]] || echo '{"hostname":"127.0.0.1"}' > "${STATE_CONFIG}"
# shellcheck source=../scripts/make_test_certificate.sh
source "${ROOT_DIR}/src/shadowbox/scripts/make_test_certificate.sh" "${RUN_DIR}"
node "${BUILD_DIR}/shadowbox/app/main.js"

View file

@ -1,23 +0,0 @@
#!/bin/bash -eu
#
# Copyright 2018 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
readonly TEST_DIR="${BUILD_DIR}/js/shadowbox/"
rm -rf "${TEST_DIR}"
tsc -p "${ROOT_DIR}/src/shadowbox" --outDir "${TEST_DIR}"
jasmine --config="${ROOT_DIR}/jasmine.json"
rm -rf "${TEST_DIR}"

View file

@ -1 +0,0 @@
/bin/

View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,15 +0,0 @@
name: "outline-ss-server"
description: "outline-ss-server is an open-source Outline Shadowsocks server"
third_party {
url {
type: HOMEPAGE
value: "https://getoutline.org/"
}
url {
type: ARCHIVE
value: "https://github.com/Jigsaw-Code/outline-ss-server/releases/tag/v1.4.0"
}
version: "1.4.0"
last_upgrade_date { year: 2022 month: 10 day: 24 }
}

View file

@ -1,28 +0,0 @@
VERSION=1.5.0
.PHONY: all
all: bin/linux-x86_64/outline-ss-server bin/linux-arm64/outline-ss-server bin/macos-x86_64/outline-ss-server bin/macos-arm64/outline-ss-server
bin/linux-x86_64/outline-ss-server: OS=linux
bin/linux-x86_64/outline-ss-server: SHA256=0c6439242afbea191281404f08ef33490b01d6d0413ccca00004c8a1927de49a
bin/linux-arm64/outline-ss-server: OS=linux
bin/linux-arm64/outline-ss-server: SHA256=a643b28c2a894af6ceb1d309bf742092719877ede85ead6e8cbbc7b64b35a7ab
bin/macos-x86_64/outline-ss-server: OS=macos
bin/macos-x86_64/outline-ss-server: SHA256=f4b034f74701e9dae52bc7c8660e875f81473ef6d535a1470967e887f5beb9c6
bin/macos-arm64/outline-ss-server: OS=macos
bin/macos-arm64/outline-ss-server: SHA256=1f1d1833935ba363a8c468cd61e90d42de7f16e7332346b3c80f389c914192d3
TEMPFILE := $(shell mktemp)
bin/%/outline-ss-server:
node ../../src/build/download_file.mjs --url="https://github.com/Jigsaw-Code/outline-ss-server/releases/download/v$(VERSION)/outline-ss-server_$(VERSION)_$(OS)_$(ARCH).tar.gz" --out="$(TEMPFILE)" --sha256=$(SHA256)
mkdir -p "$(dir $@)"
tar -zx -f "$(TEMPFILE)" -C "$(dir $@)" "$(notdir $@)"
chmod +x "$@"
rm -f $(TEMPFILE)
.PHONY: clean
clean:
rm -rf bin

View file

@ -1,31 +1,29 @@
VERSION=2.37.1
ifeq ($(ARCH),x86_64)
APP_ARCH=amd64
else
APP_ARCH=arm64
endif
.PHONY: all
all: bin/linux-x86_64/prometheus bin/linux-arm64/prometheus bin/macos-x86_64/prometheus bin/macos-arm64/prometheus
bin/linux-x86_64/prometheus: OS=linux
bin/linux-x86_64/prometheus: GOARCH=amd64
bin/linux-x86_64/prometheus: SHA256=753f66437597cf52ada98c2f459aa8c03745475c249c9f2b40ac7b3919131ba6
bin/linux-arm64/prometheus: OS=linux
bin/linux-arm64/prometheus: GOARCH=arm64
bin/linux-arm64/prometheus: SHA256=b59a66fb5c7ec5acf6bf426793528a5789a1478a0dad8c64edc2843caf31b1b8
bin/macos-x86_64/prometheus: OS=darwin
bin/macos-x86_64/prometheus: GOARCH=amd64
bin/macos-x86_64/prometheus: SHA256=e03a43d98955ac3500f57353ea74b5df829074205f195ea6b3b88f55c4575c79
bin/macos-arm64/prometheus: OS=darwin
bin/macos-arm64/prometheus: GOARCH=arm64
bin/macos-arm64/prometheus: SHA256=eb8a174c82a0fb6c84e81d9a73214318fb4a605115ad61505d7883d02e5a6f52
bin/%/prometheus: TEMPFILE := $(shell mktemp)
bin/%/prometheus:
node ../../src/build/download_file.mjs --url="https://github.com/prometheus/prometheus/releases/download/v$(VERSION)/prometheus-$(VERSION).$(OS)-$(APP_ARCH).tar.gz" --out="$(TEMPFILE)" --sha256=$(SHA256)
node ../../src/build/download_file.mjs --url="https://github.com/prometheus/prometheus/releases/download/v$(VERSION)/prometheus-$(VERSION).$(OS)-$(GOARCH).tar.gz" --out="$(TEMPFILE)" --sha256=$(SHA256)
mkdir -p "$(dir $@)"
tar -zx -f "$(TEMPFILE)" --strip-components=1 -C "$(dir $@)" prometheus-$(VERSION).$(OS)-$(APP_ARCH)/prometheus
tar -zx -f "$(TEMPFILE)" --strip-components=1 -C "$(dir $@)" prometheus-$(VERSION).$(OS)-$(GOARCH)/prometheus
chmod +x "$@"
rm -f $(TEMPFILE)

26
tools.go Normal file
View file

@ -0,0 +1,26 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build tools
// +build tools
// See https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
// and https://github.com/go-modules-by-example/index/blob/master/010_tools/README.md
package tools
import (
_ "github.com/Jigsaw-Code/outline-ss-server/cmd/outline-ss-server"
_ "github.com/go-task/task/v3/cmd/task"
)